web-dev-qa-db-ja.com

ssh経由のコマンドでtrueとORする

pkill -fをsshを介してリモートで実行しようとし、考えられるエラーコードを破棄しようとすると(プロセスが見つからない場合でもスクリプトの残りの部分を続行します)、|| 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

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

14
Gauthier

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

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

単にssh [email protected] "pkill -f asdf"を実行すると、「pkillステータスに対応する1の終了ステータスが得られます。一致するプロセスはありません」。

困難な部分は、実行時に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オプションによって渡されるコマンドが 簡単なコマンド の場合、実際には新しいプロセスをフォークしないことで最適化できることを理解するのに十分賢い、つまり、 execsの前にforkingの追加のステップを実行する代わりに、単純なコマンドを直接execsします。リモートのシンプルなコマンド(この場合は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クライアントにエラーを報告します。

30

あなたのリモートコマンドはそれ自身を殺します:

$ 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
9
ctx

「asdf」に一致するものをすべて殺すようにpkillに要求します。 [a] sdfと一致するように指示する必要があります。そのようにすると、「asdf」という名前のものが検索されますが、それ自体は表示されません(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" ...
3
Olivier Dulac