シェルフォークが元のものより長く生き残るのを防ぎますか?

シェルフォークが元のものより長く生き残るのを防ぎますか?

次のようなBashスクリプトがある場合:

function repeat {
    while :; do
        echo repeating; sleep 1
    done
}
repeat &
echo running once

running once一度印刷されますが、repeatフォークは永遠に存在し、無限に印刷されます。

repeatこれを生成したスクリプトが終了した後も引き続き実行されないようにするにはどうすればよいですか?

bash -c新しいインタプリタを明示的にインスタンス化すると、親プロセスが消えたため強制的に終了すると思われましたが、孤児プロセスはinitPID 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

ただし、他の重要なプロセスではこの名前を使用しないことをお勧めします。それ以外の場合は名前も消去されます。

  1. バックグラウンドジョブを生成したスクリプト(またはプロセス)が終了したときに終了する必要がある分岐シェルで、バックグラウンドジョブを処理するためのより優れた許容可能な方法があるかどうか疑問に思います。

  2. すべてのプロセスで同じ名前を盲目的に使用するよりも良い方法がない場合は、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

関連情報