web-dev-qa-db-ja.com

以前に開始したSSHコマンドのPIDを予測する

これは最も奇妙なことです。

スクリプトでは、次のようにSSHトンネルを開始します。

ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar

これにより、バックグラウンドに入るsshインスタンスが開始され、スクリプトの実行が続行されます。次に、bashの $!変数 を使用して、そのPIDを(後で強制終了するために)保存します。これを機能させるために、sshコマンドがすでにバックグラウンドに入っている場合でも&を追加します(それ以外の場合、$!には何も含まれていません)。したがって、たとえば次のスクリプト:

#!/bin/bash

ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar &
echo $!
pgrep -f "ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar"

出力

(some ssh output)
28062
28062

...予想どおり、同じPIDの2倍。しかし、今、端末からこのコマンドの正確なシーケンスを実行すると、$!によって出力されるPIDは間違った(という意味で- notsshインスタンスのPID)。ターミナルから:

$ ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar &
[1] 28178
(some ssh output)
$ echo $!
28178
$ pgrep -f "ssh -o StrictHostkeyChecking=no -fND 8080 foo@bar"
28181

また、常に3つの数字が離れているわけではありません。また、1または2の違いも確認しました。ただし、予想どおり、またこの一連のコマンドがスクリプト内で実行される場合と同様に、同じPIDになることはありません。

  1. 誰かがこれが起こっている理由を説明できますか?最初のssh呼び出しが実際に別のプロセスをフォークしていることが原因である可能性があると思いましたが、スクリプト内から機能するのはなぜですか?

  2. これにより、スクリプトで$!を使用して上記のssh PIDを取得することが、実際に常に機能するかどうかも疑問に思いました(これまでのところは機能していますが)。これは本当に信頼できますか? pgrep ..を使用するよりも「クリーン」だと感じました。

4
Malte Skoruppa

シェルの$!変数は、シェルによって開始されたプロセスのpidのみを認識します。ご想像のとおり、-fを使用したssh呼び出しは、独自のプロセスをフォークしてバックグラウンドに移動できるため、プロセスツリー全体は[1]のようになります。

Shell
|
+--ssh<1> (pid is $!)
   |
   +--ssh<2> (pid is different)

ssh<1>は、呼び出し後すぐに終了します。したがって、$!の値が役立つ可能性はほとんどありません。リモート通信を実行してトンネリングを実行しているのはssh<2>であり、そのPIDを確実に取得する唯一の方法は、pgrep [2で行っているようにプロセステーブルを調べることです。 ]。ここでは、pgrepメソッドが正しい方法である可能性があります。

スクリプトで機能するがインタラクティブでは機能しない理由については、これはおそらく競合状態です。最初のsshをバックグラウンドに配置したため、シェルとsshは同時に実行され、sshはCPU負荷の高い暗号化認証とネットワークラウンドトリップを実行します。スクリプトで実行するpgrepは、ssh<1>がバックグラウンドに移動する前に実行されている可能性があります。これを回避するには、後でpgrepを呼び出すか、後で実際にPIDが必要になったときにのみ呼び出すことで、sleepを実行します。

[1]:技術的には、sshがバックグラウンドに従来のダブルフォークを使用している場合、これよりも複雑になる可能性があります。その場合、2つの間に別の一時的なsshプロセスがあります。

[2]:あなたがsystemdであり、cgroupsなどを使用してすべての子供を追跡している場合を除きます。あなたはそうではありません。

7
Tom Hunt