これは最も奇妙なことです。
スクリプトでは、次のように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になることはありません。
誰かがこれが起こっている理由を説明できますか?最初のssh
呼び出しが実際に別のプロセスをフォークしていることが原因である可能性があると思いましたが、スクリプト内から機能するのはなぜですか?
これにより、スクリプトで$!
を使用して上記のssh
PIDを取得することが、実際に常に機能するかどうかも疑問に思いました(これまでのところは機能していますが)。これは本当に信頼できますか? pgrep
..を使用するよりも「クリーン」だと感じました。
シェルの$!
変数は、シェルによって開始されたプロセスの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などを使用してすべての子供を追跡している場合を除きます。あなたはそうではありません。