sshを介したコマンドでtrueとORする


15

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

$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill asdf || true"
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill -f asdf || true"
255

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

回答:


29

ssh255の終了ステータスを返すのはそれ自体であるという仮定は正しいです。sshmanページには、と述べています:

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

単に実行する場合、「プロセスが一致していません」のステータスに対応するssh pi@10.20.0.10 "pkill -f asdf"終了ステータスを取得する可能性が高くなります。1pkill

難しい部分は、実行時にSSHでエラーが発生する理由を理解することです

ssh pi@10.20.0.10 "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オプションがあり、単純なコマンド、それが実際に新しいプロセスをforkしないことによって最適化することができ、すなわち、それが直接exec、単純なコマンドの代わりに、余分な工程を経ねforkそれexecの前にing の。リモートの単純なコマンド(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 pi@10.20.0.10 "pkill -f asdf || true"sshdプロセスのフォークと幹部をbash -c "pkill -f asdf || true"

ctxの回答が指摘しているpkillように、独自のプロセスを強制終了することはありません。しかし、それはなりますそのコマンドラインと一致する他のプロセスを殺す-fのパターンを。bash -c独自の親(それが起こるように) -それは、このプロセスを殺すよう命令は、このパターンにマッチします。

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


1
以下のように答えが正しくソースの問題を識別している間pkill、親シェルプロセスを殺すその引数リストは、正規表現にマッチするので、私は専門用語の異議を上げるでしょう:x || yあるない化合物のコマンド、それはだコマンドリスト
ステファンシャゼラス

@StéphaneChazelasフィードバックありがとうございます。私は複合コマンドとして条件付き構造を含めることでBashを使用していましたがx||y、コマンドリストとして考慮する方が論理的に一貫していることに同意します。さまざまなPOSIX定義へのリンクを含めるように回答を編集しました。
アンソニーG-モニカの正義

1
一般的なケースでは、実行する別のコマンドがまだある可能性があるコマンドリストであるため、最適化できないほどではありません。ではzsh/ ksh93/ FreeBSDではshfalse || pkill -f asdfはずpkillシェルプロセスで実行。bash単純なコマンドが1つしかない場合にのみ最適化を行います。true; pkill -f asdfまた問題になります。
ステファンシャゼラス

9

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

$ 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

それは理にかなっている!bash -c 'pgrep -af asdf'(なし|| true)はそれ自体を見つけませ。何故なの?持ってい-fます。
ゴーティエ

2
@Gauthier実際、この場合、Bashはコマンドが単純なコマンド(複合コマンドではない)であることを認識するのに十分賢いので、実際に新しいプロセスをフォークしないことで最適化すると思います。以前に同様の動作に遭遇したことを覚えています(答えを更新する必要があります)。
アンソニーG-モニカの正義

3

「asdf」に一致するものをすべて殺すようにpkillに依頼します。[a] sdfに一致するように指示する必要があります。この方法では、「asdf」という名前のものを探しますが、それ自体は表示されません(asdfを[a] sdfに揃えると、ではありません。)

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" ...

注:私は私の 'fail' grepの例で、正規表現をそのままにしておくこともできます。既に正規表現は一致しません(a、b、またはcはコマンドラインの ']'と一致しません) 。しかし、正規表現のテストを考えるのは簡単ではありません。一般に、トリックは機能します。自動化されたものはほとんどの場合機能します。そうでない場合は、巧妙なハッキング(または手動による介入)が必要になります。
オリビエデュラック

また、(abc)?(def)?する必要があり([a]bc)?([d]ef)?ます...あなたは正規表現で正規表現を解析することはできません!> :-)
wizzwizz4

@ wizzwizz4知っています。ただし、exempleは既に一致しません。これは複雑なもので、私はシンプルなケースのためのシンプルなソリューション提供
オリヴィエ・デュラック

@私はすでに私の最初のコメント...にそう言うwizzwizz4
オリヴィエ・デュラック
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.