it-swarm-ja.com

スクリプトが停止したときにインラインバックグラウンドプロセスを停止するにはどうすればよいですか?

シェルスクリプトでは、次のことを行います。

#!/bin/sh

while true; do ssh -o ExitOnForwardFailure=yes -L 8080:localhost:80 -N server; sleep 1; done &

... rest of the script, which uses the tunnel as made above ...

これにより、トンネルは常に開いたままになり、接続が失われた場合に再び開かれることが保証されます。このトンネルは、メインスクリプトの他の部分で使用されますが、ここでは省略されています。これらのパーツは機能していないトンネルを処理でき、後で再試行するだけです。

たとえばSIGTERMまたはSIGINTが原因でメインスクリプトが停止した場合、そのwhileループも停止する必要があります。メインスクリプトが終了した後、そのトンネルを開いたままにする必要はありません。

シェルスクリプトでこれを行うための一般的なアプローチは何ですか?私は2つのことが欲しいことに注意してください:

  1. sshコマンドの再実行を防止します
  2. 現在進行中のsshセッションをできるだけ早く停止して切断します

シェルスクリプトでこれらすべてを行う方法がわかりません。

現在、プレーンなshで作業していますが、必要に応じてbashに進むことができます。

1
Pritzl

やや一般的なアプローチ。

while true; do foo; sleep 1; done &
# the rest of the script here
kill -- -"$$"

秘訣は、スクリプトがシェルのPIDと等しいプロセスグループID(PGID)で子プロセス(ここでは特にfoo)を実行することです。これは孫などに伝播します。シェル自体もこのプロセスグループに含まれます。例外があります(インタラクティブシェルでのジョブ、 timeout )ので、これはあなたが望むほど一般的ではありませんが、それでもfoosshまたは非対話型スクリプトでの同様の単純なコマンドは、このアプローチが機能するはずです。

負の引数を持つkillは、プロセスグループ全体にシグナルを送信します。

ただし、注意点が1つあります。それは、競合状態の可能性です。一般に、fooは、サブシェルが信号を受信して​​処理する前に強制終了される可能性があります。遅延が(何らかの理由で)十分に長い場合、fooがそのジョブを実行した後、新しいkillが生成される可能性があります(特にsleep 1がない場合)。この改善を検討してください。

while true; do foo; sleep 1; done &
subpid=$!
# the rest of the script here
kill "$subpid"
wait "$subpid" 2>/dev/null
# at this moment we're certain the subshell is no more, new foo will not be spawned
trap '' TERM
# foo will maintain the old PGID, so…
kill -- -"$$" 2>/dev/null

トラップは、コンソールにTerminatedを出力せずに、メインシェルを正常に終了させるためだけにあります。


バックグラウンドプロセスの一般的なアプローチではありませんが、通常、同様のシナリオでsshに役立つ方法です。

autosshを使用します。から そのマニュアル

autosshは、sshのコピーを開始して監視し、死んだりトラフィックの通過を停止したりした場合に必要に応じて再起動するプログラムです。

[…]

autosshは、監視しているsshプロセスの死亡の方法を区別し、適切に動作しようとします。ルールは次のとおりです。

  1. sshプロセスが正常に終了した場合(たとえば、インタラクティブセッションで誰かがexitと入力した場合)、autosshは再起動せずに終了します。
  2. autossh自体がSIGTERMSIGINT、またはSIGKILLシグナルを受信した場合、それは意図的にシグナルされたと見なし、子を殺した後に終了しますsshプロセス;
  3. […]
  4. […]
  5. sshプロセスが他の理由で停止した場合、autosshは新しいプロセスを開始しようとします。

したがって:

autossh … &
apid=$!
# the rest of the script here
kill "$apid"

そもそもトンネルを確立できない場合は通知されないことに注意してください。これは元のアプローチでも起こりうる欠陥であるため、ここではこの問題に対処していません。

2