次のようなBashスクリプトがある場合:
function repeat {
while :; do
echo repeating; sleep 1
done
}
repeat &
echo running once
running once
一度印刷されますが、repeat
フォークは永遠に存在し、無限に印刷されます。
repeat
これを生成したスクリプトが終了した後も引き続き実行されないようにするにはどうすればよいですか?
bash -c
新しいインタプリタを明示的にインスタンス化すると、親プロセスが消えたため強制的に終了すると思われましたが、孤児プロセスはinit
PID 1によって採用されているようです。
別のファイルでこれをテストしてください。
# repeat.bash
while :; do echo repeating; sleep 1; done
# fork.bash
bash -c "./repeat.bash & echo an exiting command"
実行すると、バックグラウンドで実行され./fork.bash
続けます。repeat.bash
簡単で怠惰な解決策は、次の行を追加することですfork.bash
。
pkill repeat.bash
ただし、他の重要なプロセスではこの名前を使用しないことをお勧めします。それ以外の場合は名前も消去されます。
バックグラウンドジョブを生成したスクリプト(またはプロセス)が終了したときに終了する必要がある分岐シェルで、バックグラウンドジョブを処理するためのより優れた許容可能な方法があるかどうか疑問に思います。
すべてのプロセスで同じ名前を盲目的に使用するよりも良い方法がない場合は、
pkilling
ネットワークサーバーと同じように実行されている冗長タスクを処理して終了する必要がありますか?cron
スクリプトがgit
リポジトリにあり、コードを変更せずに自分で含める必要があるため、作業を避けたいと思います/etc/
。
答え1
スクリプトが終了する前にバックグラウンドプロセスが終了します。
trap '[ "$pid" ] && kill "$pid"' EXIT
function repeat {
while :; do
echo repeating; sleep 1
done
}
repeat &
pid=$!
echo running once
どのように動作しますか?
trap '[ "$pid" ] && kill "$pid"' EXIT
これは罠。スクリプトが終了しようとするたびに、単一引用符で囲まれたコマンドが実行されます。このコマンドは、シェル変数にNULL以外の
pid
値が指定されていることを確認します。存在する場合、そのプロセスに関連するプロセスはpid
終了します。pid=$!
これにより、前のバックグラウンドcommand()のプロセスIDが
repeat &
シェル変数に保存されますpid
。
改善する
〜のようにパトリックコメントで指摘したように、スクリプトが終了する可能性があります。後ろにバックグラウンドプロセスが開始されますが、今後変数がpid
設定されました。次のコードを使用してこの状況を処理できます。
my_exit() {
[ "$racing" ] && pid=$!
[ "$pid" ] && kill "$pid"
}
trap my_exit EXIT
function repeat {
while :; do
echo repeating; sleep 1
done
}
racing=Y
repeat &
pid=$!
racing=
echo running once
答え2
シェルには、シャットダウン時にコマンドを終了するようにシェルに指示するコマンドがtcsh
あります。hup
hup mycommand...
tcsh
これは機能をサポートしていないため、機能の実行には役立ちません。
でzsh
ジョブ制御を有効にすると、すべてのバックグラウンドジョブに対してデフォルトでこれが行われます。
$ zsh -c 'set -m; sleep 2 &'; ps
[1] 23672
zsh:1: warning: 1 jobs SIGHUPed
ただし、スクリプトでジョブ制御を有効にすると副作用が発生する可能性があります。
bash
これを行うことも可能ですが、インタラクティブにのみ(スクリプトで-iを使用して呼び出してもこれを実行できます)、シェルにログインしている場合にのみ(!?)可能です。そして、このオプションを有効にする必要がありますhuponexit
。だから:
bash -O huponexit -lic 'sleep 2 &'
しかし、これは副作用が多いので、おそらく良い考えではないでしょう。
別のオプションzsh
は、シャットダウン時に実行されているすべての既知のサブプロセスをシャットダウンすることです。どんな場合でも、孫プロセスについて簡単にはわかりません(いつも終了したくありません)。連想配列で使用可能にするzsh
:$jobstates
TRAPEXIT() kill -s HUP ${${jobstates[(R)running:*]/#*:/}/%=*/}
trap exit INT HUP TERM
他のシェルは子プロセスのリストを公開しませんが、ppidを使用してプロセスを終了できます$$
。たとえば、次のようになります。
trap 'pkill -HUP -P "$$"' EXIT INT HUP TERM