これは最も奇妙なものです。
スクリプトは、次のように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出力は$!
次のようになります。間違った(ある意味ではそうですね。いいえインスタンスのPID ssh
)。ターミナルから:
$ 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
使うよりも「きれいだ」と思います...
答え1
シェルの$!
変数は、シェルによって開始されたプロセスのpidだけを知っています。予想通り、呼び出しは独自のプロセスのフォークssh
を使用して行われる-f
ため、バックグラウンドに移動できるため、プロセスツリー全体は[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
後でsleep
呼び出すか、後で PID が実際に必要なときに呼び出して実行します。
ssh
[1]:技術的に古典的なデュアルフォークを背景として使用すると、これより複雑になる可能性があります。その場合、ssh
2つの間に別の短いプロセスがあります。
[2]:systemd
すべての子供を追跡するためにcgroupまたは他のものを使用しない限り。しかし、あなたはそうではありません。