SSH 経由のコマンドで true と OR 演算する

SSH 経由のコマンドで true と OR 演算する

ssh 経由でリモートで実行しようとしpkill -f、可能性のあるエラー コードを破棄しようとすると (プロセスが見つからない場合でもスクリプトの残りの部分を続行するため)、|| true期待どおりに動作しません。

$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh [email protected] "pkill asdf || true"
$ echo $?
0
$ ssh [email protected] "pkill -f asdf || true"
255

それはそうだと思いますssh引用符で囲まれたコマンドではなく、255 を返しますが、なぜでしょうか?

答え1

ssh終了ステータス 255 を返すのは自分自身であるというあなたの推測は正しいです。sshマニュアル ページには次のように記載されています。

ssh は、リモート コマンドの終了ステータスで終了します。エラーが発生した場合は、255 で終了します。

を単に実行すると、おそらく「ssh [email protected] "pkill -f asdf"1pkill一致するプロセスはありません」。

難しいのは理解することですなぜSSHを実行するとエラーが発生します

ssh [email protected] "pkill -f asdf || true"

SSHリモートコマンド

SSH サーバーはシェルを起動してリモート コマンドを実行します。次に、この動作の例を示します。

$ ssh server "ps -elf | tail -5"
4 S root     35323  1024 12  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony [priv]
5 S anthony  35329 35323  0  80   0 - 43170 poll_s 12:01 ?        00:00:00 sshd: anthony@notty
0 S anthony  35330 35329  0  80   0 - 28283 do_wai 12:01 ?        00:00:00 bash -c ps -elf | tail -5
0 R anthony  35341 35330  0  80   0 - 40340 -      12:01 ?        00:00:00 ps -elf
0 S anthony  35342 35330  0  80   0 - 26985 pipe_w 12:01 ?        00:00:00 tail -5

デフォルトのシェルはでありbash、リモートコマンドは単純なコマンドではなく、パイプライン「制御演算子で区切られた 1 つ以上のコマンドのシーケンス|」。

-cBashシェルは、オプションによって渡されたコマンドが簡単なコマンド、実際には新しいプロセスをフォークしないことで最適化できます。つまり、を実行する前に を実行するexecという余分な手順を踏むのではなく、単純なコマンドを直接実行します。リモートの単純なコマンド (この場合は )を実行したときに何が起こるかの例を次に示します。forkexecps -elf

$ ssh server "ps -elf" | tail -5
1 S root     34740     2  0  80   0 -     0 worker 11:49 ?        00:00:00 [kworker/0:1]
1 S root     34762     2  0  80   0 -     0 worker 11:50 ?        00:00:00 [kworker/0:3]
4 S root     34824  1024 31  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony [priv]
5 S anthony  34829 34824  0  80   0 - 43170 poll_s 11:51 ?        00:00:00 sshd: anthony@notty
0 R anthony  34830 34829  0  80   0 - 40340 -      11:51 ?        00:00:00 ps -elf

私は以前にもこの行動に遭遇したことがあるが、他にもっと良い参考資料が見つからなかった。この AskUbuntu の回答

pkill の動作

pkill -f asdf || trueは単純なコマンドではないので(コマンドリスト) の場合、上記の最適化は行われないため、 を実行すると、プロセスはフォークして を実行します。ssh [email protected] "pkill -f asdf || true"sshdbash -c "pkill -f asdf || true"

ctxの回答が指摘しているように、pkill自身のプロセスを終了しません。しかし、意思コマンドラインがパターンに一致する他のプロセスをすべて強制終了します-fbash -cコマンドはこのパターンに一致するため、このプロセス (偶然にもその親) を強制終了します。

次に、SSH サーバーは、リモート コマンドを実行するために開始したシェル プロセスが予期せず終了したことを認識し、SSH クライアントにエラーを報告します。

答え2

リモート コマンドが自動終了します:

$ ssh 10.0.3.70 'pgrep -af asdf'
$ ssh 10.0.3.70 'pgrep -af asdf || true'
1018 bash -c pgrep -af asdf || true

pgrep と pkill は自身のプロセスを無視しますが、-f フラグを使用すると親シェルを見つけます。

$ pgrep -af asdf
$ pgrep -af asdf || true
$ bash -c 'pgrep -af asdf'
$ bash -c 'pgrep -af asdf || true'
9803 bash -c pgrep -af asdf || true

答え3

pkill に、"asdf" に一致するものをすべて削除するように指示します。[a]sdf に一致するように指示する必要があります。そうすれば、"asdf" という名前のものを検索しますが、それ自体は表示されません (asdf を [a]sdf に揃える場合、s が ] に揃えられ、s に揃えられないことに注意してください)。

ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'

これは grep/egrep/awk などでも使用される一般的なトリックです。

ps -ef | grep "something"  # will sometimes match itself too
ps -ef | grep "[s]omething" # will not match itself

# why it works:
# the commandline contains:     ps -ef | grep [s]omething
# and grep tries to find:                      something

このトリックは古く、数十年前に Unix の FAQ (今でも読む価値のある記事です!) で見たことがあります。

これを「自動化」するのは簡単ではありませんが、通常は変数文字列 regexp="something" を grep する必要があるたびに、次のように実行できます。

grep "$(echo "${regexp}" | LC_ALL='C' sed -e 's/[a-zA-Z0-9_-]/[&]/')" 
#  if regexp="something",  it does: grep "[s]omething"
#  if regexp="otherthing", it does: grep "[o]therthing"
#  if regexp="^thirdthing", it does: grep "^[t]hirdthing" #ok, kept the "^"
#BUT fails on : regexp="[abc]def", as it does: grep "[[a]bc]def" instead of grep "[abc][d]ef" ...

関連情報