SSHを介してこれを行う方法を知らなかったという事実が思い浮かびました。
~しようとする
$ ssh user@server sh -c 'echo "hello"'
しかし、何も出力しません。むしろ空行を出力します。与えられたコマンドがssh
リモートホストで実行されると、なぜこれが起こるのかを理解できます(または自分で合理化しようとすることができます)。$SHELL -c
いいですね。 2回目の試みです。
$ ssh user@server 'echo "hello"'
hello
みんな大丈夫です。
今私が本当に欲しいものは:
$ ssh user@server 'echo "hello $1"' sh "world"
hello sh world
うーん…どこから来たのsh
?これは空であることを示し、$1
実際に反対側で実行されるものは次のとおりです。
$SHELL -c 'echo "hello sh world"'
私が望んでいたのではなく、
$SHELL -c 'echo "hello $1"' sh "world"
ssh
以下を介して実行されるスクリプトに引数を安全に渡す方法はありますか?
sh -c 'script text' sh "my arg1" "my arg2" "my arg3" ...
しかし、リモートホストでは?
私のローカルとリモートのログインシェルの両方/bin/sh
。
安全=パラメータにスペースなどを保持します。
Sanely =引用符を狂ったように逃げません。
答え1
このプロセスで最初に理解する必要があるのは、sshがパラメータを処理する方法です。実行したいパラメータではなく、sshのパラメータを意味します。を呼び出すと、ssh
リモートホスト仕様()の後にある引数がuser@server
連結され、リモート側シェルを介して渡されます。パラメータがローカル側で正しく分割されたとしても、リモート側でも正しく分割されるという意味ではないので注目する価値があります。
あなたの例を使用して:
ssh user@server 'echo "hello $1"' sh "world"
これらのパラメータはコマンドで連結されます。
echo "hello $1" sh world
それがあなたが得る理由です。
hello sh world
hello
との間の二重スペースは元々あるべき場所ですが、そうではないからですsh
。$1
$1
がない別の例は$1
次のとおりです。
ssh user@server echo "foo bar" baz
結果は次のとおりです。
foo bar baz
これは、パラメータが互いに接続されているため、次のコマンドが実行されるためです。
echo foo bar baz
シェルを介して渡されたコマンドをバイパスする方法がないため、渡した内容がシェル評価で生き残るかどうかを確認するだけです。私が通常これを達成する方法は次のとおりです。printf "%q "
たとえば、
cmd=(echo "foo bar" baz)
ssh user@server "$(printf "%q " "${cmd[@]}")"
結果は次のとおりです。
foo bar baz
別の変数を使用する方がきれいで理解しやすくなりますが、cmd
必須ではありません。以下は同じように機能します。
ssh user@server "$(printf "%q " echo "foo bar" baz)"
これはシェルパラメータの例にも当てはまります。
cmd=(sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3")
ssh user@server "$(printf "%q " "${cmd[@]}")"
結果は次のとおりです。
1=<my arg1> 2=<my arg2> 3=<my arg3>
代替ソリューションとして、コマンドを完全なシェルスクリプトに渡すことができます。たとえば、
ssh user@server <<'EOF'
sh -c 'echo 1="<$1>" 2="<$2>" 3="<$3>"' sh "my arg1" "my arg2" "my arg3"
EOF
ただし、このソリューションには欠点もあります。プログラムで実行するのがより難しいからです(STDINを通過するための文書の作成)。また、STDINを使用しているため、リモート側のスクリプトがSTDINを読み取るようにしたい場合(少なくとも少しのトリックを使用すると)、そうすることはできません。