![SSH 経由のコマンドで true と OR 演算する](https://rvso.com/image/122433/SSH%20%E7%B5%8C%E7%94%B1%E3%81%AE%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%20true%20%E3%81%A8%20OR%20%E6%BC%94%E7%AE%97%E3%81%99%E3%82%8B.png)
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"
1
pkill
一致するプロセスはありません」。
難しいのは理解することですなぜ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 つ以上のコマンドのシーケンス|
」。
-c
Bashシェルは、オプションによって渡されたコマンドが簡単なコマンド、実際には新しいプロセスをフォークしないことで最適化できます。つまり、を実行する前に を実行するexec
という余分な手順を踏むのではなく、単純なコマンドを直接実行します。リモートの単純なコマンド (この場合は )を実行したときに何が起こるかの例を次に示します。fork
exec
ps -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"
sshd
bash -c "pkill -f asdf || true"
ctxの回答が指摘しているように、pkill
自身のプロセスを終了しません。しかし、意思コマンドラインがパターンに一致する他のプロセスをすべて強制終了します-f
。bash -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" ...