SSHを介して環境変数をコマンドに渡すつもりです。
$ ssh HOST A=1 set | grep ^A
$ ssh HOST A=1 sh -c set | grep ^A
A='1'
$ ssh HOST A=1 echo '$A'
$ ssh HOST A=1 sh -c 'echo $A'
したがって、set
環境変数は見えず、別々のプロセスに見えます。同じことが起こらないようですecho
。
sshd
正確に何が送信されたのか、どのように実行されたのか(どのコマンドが実行されたのか)を確認する方法はありますか?
答え1
を実行すると、ssh user@host command
拡張プログラムによって生成されたトークンはcommand
中間スペースに関連付けられ、文字列ssh
としてリモートホストに送信されます。その後、リモートホストはリモートユーザーのデフォルトシェル(で指定されたもの/etc/passwd
)のインスタンスを実行し、クライアントから受け取った文字列をシェル-c
オプションの引数として渡します(詳細は以下を参照)。
例えば
$ foo="bar baz"
$ ssh user@host echo "$foo"
bar baz
ローカルは2つのパラメータをssh
受け取ります。これを連結して文字列を形成し、それをサーバーに送信します。サーバーは次のタスクを実行し、コマンドの出力は標準出力にローカルに印刷されます。echo
bar baz
echo bar baz
/bin/sh -c 'echo bar baz'
ssh
クライアントが実際にサーバーに送信した内容をどのように確認できますか?
〜のようにKamil Machorovskyは次のように指摘した。質問のコメントからssh -v
サーバーに送信されたコマンド文字列は、デバッグメッセージとして出力されます。
$ ssh -v host A=1 sh -c set | grep ^A
...
debug1: Sending command: A=1 sh -c set
...
sshd
実際に実行される内容をどのように確認できますか?
クライアントに自分のタスクの一種のトレースを表示するようにサーバーに要求する方法がわかりません。ただし、テスト目的でstrace
デーモンインスタンスを使用できます。
$ sudo strace -e execve -f /usr/bin/sshd -p 2222
これは、リモート実行用のコマンドラインを作成するときに正しく引用できないことが簡単であるため、特に便利です。たとえば(あなたの質問に与えられた例のバリエーション):
$ ssh -p 2222 host bash -c 'A=1 set'
# Prints nothing
strace
私たちが見たコマンド文字列は実際にはリモートホストで分割されており、最初にユーザーのデフォルトシェルでコマンドライン全体を実行したことがわかります(ローカルシェルが引数リストを作成したときに一重引用符が削除されましたssh
)。
... execve("/bin/bash", ["bash", "-c", "bash -c A=1 set"] ...
それから:
... execve("/usr/bin/bash", ["bash", "-c", "A=1", "set"] ...
コマンド文字列(のパラメータ-c
)はですA=1
。 0番目の引数としてset
渡されます(コマンド名として使用bash
可能)。$0
リモートシェルが実際に実行した内容を確認するには?
繰り返しますが、私は簡単な方法を知りません。単純なケースでは、シェルオプションを使用するのはxtrace
簡単です。たとえば、ssh host A=1 set
次のようになります。
$ command=$(printf '%s ' A=1 set); printf '%s\n' "${command% }" |
ssh host sh -x
sh
リモートユーザーのプライマリシェルと交換する必要があります。
コマンドはssh
標準入力にパイプされます。このフォームを使用すると、変更せずに入力できます(ssh host sh -xc A=1 set
上記の理由で機能せず、引用符を追加すると、この練習の実際の目的は失われます)。
簡単ではない場合、特にssh
調査中の呼び出しがすでに標準入力を使用している場合は、リモートホストにシェルを開いたxtrace
状態で強制的に実行させることができます。実験目的とエラーが原因でリモートシステムにログインできないことを認識するため1つの(やや粗雑な)アプローチは、リモートユーザーのデフォルトシェルを交換することです/etc/passwd
。と仮定して/bin/sh
に変更し/home/your_user/bin/sh
、後者を実行可能スクリプトとして生成します。
#!/bin/sh
exec /bin/sh -x "$@"
これにより、シェルは実行中のコンテンツのトレースを表示します。
$ ssh host A=1 echo foo
foo
+ A=1
+ echo foo
最後に、サンプルコマンドに関して次のようにします。
ssh HOST A=1 set
印刷されない理由は、A=1
ユーザーのデフォルトシェルがBash(おそらく呼び出されるsh
)であるためです。dash
、、ksh
Bash(少なくとも私が持っているバージョン)yash
で発生するのとは対照的に、変数セットは出力されませんzsh
。set
5.0.17
set
コマンド自体の場合。一方、次のことも確認できます。$ ssh host 'A=1 > set' | grep ^A A=1
Ieが呼び出し前に宣言されると、出力
A
に表示されます。set
set
で環境に追加されたので、
A=1 sh -c set
当然印刷されます。A
sh
set
A=1 echo '$A'
A
拡張後に実行環境にのみ追加されるため、値は印刷されませんA
(組み込みと仮定)。印刷されたものと比較してください。echo
$A
ssh host 'A=1; echo $A'
1
A=1 sh -c 'echo $A'
上記のように、リモートホストはオプション-c
と引数を使用してA=1 sh -c echo $A
リモートユーザーのデフォルトシェルを実行し、そのシェルが実行されるため何も印刷されませんsh -c echo $A
。コマンド文字列はecho
引数なしですぐになります。これを機能させるには、二重引用符がリモートホストに送信
echo $A
されていることを確認する必要があります。そして$A
早期拡張防止(ローカルシェルまたは「外部」リモートシェルを介して):例:ssh host A=1 sh -c '"echo \$A"'
またはssh host A=1 sh -c "'echo \$A'"
。リモートシェルを明示的に呼び出すときは、標準入力からコマンドを読み取る方が簡単です。
$ echo 'echo $A' | ssh host A=1 sh -s 1