SSH経由で `sh -c`スクリプトを実行する(パラメータを安全かつ正常に渡す)

SSH経由で `sh -c`スクリプトを実行する(パラメータを安全かつ正常に渡す)

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を読み取るようにしたい場合(少なくとも少しのトリックを使用すると)、そうすることはできません。

関連情報