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つ以上の命令シーケンス|
」。
Bashシェルは、渡されたオプションが次のようになることを認識するのに十分スマートです-c
。簡単なコマンド、実際に新しいプロセスを分岐しないことで最適化できます。つまり、実行前に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 -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」と一致するすべてのエントリを終了するように依頼しました。 "asdf"という名前のエントリは引き続き検索されますが、それ自体は表示されないように[a] sdfと一致するように指示する必要があります(asdfを[a] sdfと並べ替える場合は、sは]と並べられていないことに注意してください。 )
ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'
これは、grep/egrep/awk/etcでも使用される一般的なトリックです。
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" ...