bash のジョブ制御で EXIT をトラップして強制終了する

bash のジョブ制御で EXIT をトラップして強制終了する

複数のプログラムを実行するbashスクリプトがあります

#!/bin/sh
python program1.py &
python program2.py &
other programs ... &
lastProgram

私はこれを./myscript.sh

Ctrl+を押してC閉じるとlastProgram、終了し、他のすべてのプログラムはバックグラウンドで実行され続けます。問題は、他のプログラムを終了する必要があることです。

スクリプトから起動されたすべてのプログラムの終了を処理する適切な方法はどれですか?

答え1

プロセス ID を収集し、終了時にバックグラウンド プロセスを終了します。

#!/bin/bash
killbg() {
        for p in "${pids[@]}" ; do
                kill "$p";
        done
}
trap killbg EXIT
pids=()
background job 1 & 
pids+=($!)
background job 2... & 
pids+=($!)
foreground job

トラップはEXIT、理由に関係なく、シェルが終了したときに関数を実行します。trap killbg SIGINTでのみ実行するように に変更することもできます^C

これは、スクリプトがバックグラウンド プロセスを停止する前に、バックグラウンド プロセスの 1 つが終了したかどうかをチェックしません。終了した場合、エラーが発生したり、最悪の場合、間違ったプロセスを停止したりする可能性があります。


または、ジョブ ID でそれらを強制終了します。出力を読んで、jobsどれがまだアクティブであるかを確認しましょう。

#!/bin/bash 
killjobs() {
    for x in $(jobs | awk -F '[][]' '{print $2}' ) ; do 
        kill %$x
    done
}
trap killjobs EXIT
sleep 999 &
sleep 1 &
sleep 999 &
sleep 30

他のプロセスを生成するバックグラウンド プロセス (サブシェルなど(sleep 1234 ; echo foo) &) を実行する場合、これを機能させるには (「モニター モード」) を使用してジョブ制御を有効にする必要がありますset -m。そうでない場合は、リード プロセスのみが終了します。

答え2

私はちょうどPIDを収集し、スクリプトの最後にそれらをすべて削除することについての同様の質問を読んでいました -問題それは終了したプロセスのPIDですリサイクルされ、新たなプロセスで再利用される可能性があるスクリプトが終了する前に、新しい(ランダムな)プロセスを強制終了する

bash のジョブ制御で EXIT をトラップして強制終了する

bash のジョブ制御を使用すると、実行可能なジョブの最大数をカウントして、トラップと %n ジョブスペックを使用してスクリプトで開始されたプロセスのみを強制終了できます (この例では 3 つのみ)。

#!/bin/bash
#trap 'kill %1 %2 %3' 0     # 0 or EXIT are equivalent
#trap 'kill %1 %2 %3' EXIT  # or use {1..n} as below
trap 'kill %{1..3}' EXIT
sleep 33 &
sleep 33 &
sleep 33 &
echo processes are running, ctrl-c the next sleep
sleep 66
echo this line will never be executed

すでに終了している存在しないジョブスペックに対する追加の「kill」はエラー メッセージのみを表示し、他の新しい/ランダムなプロセスは強制終了しません。


スクリプトのプロセスグループ全体を強制終了します

スクリプトのプロセスグループ全体を強制終了する少し異なる方法があります。ただし、スクリプト/シェルのジョブ制御が設定されていない場合は、できた親から PPID を継承します...ただし、ジョブ制御がなければ、上記も機能しません。

違いは、トラップのこの kill コマンドが bash の PID を使用する点です。これは、新しいプロセスの PGID になるためです。

trap 'kill -- -$$' EXIT

見るこの関連Qまたはここで、ヨハネス・フィッシュ・ジームケはSIGINTとSIGTERMsetsidをトラップし、「新しいプロセスグループでプロセスグループ全体を強制終了して、自分自身を殺すリスクを回避する

答え3

実行が完了する前にバックグラウンド プロセスが完了しなかった場合にすべてのバックグラウンド プロセスを強制終了する場合はlastProgram、すべての pid を保存する必要があります。

python program1.py &
p1pid=$!
python program2.py &
p2pid=$!
other programs ... &
p3pid=$!
lastProgram

kill $p1pid $p2pid $p3pid

スクリプトを終了する前に、すべてのバックグラウンド プロセスの実行が完了するまで待機したい場合は、waitコマンドを使用できます。

python program1.py &
python program2.py &
other programs ... &
lastProgram
wait

答え4

これが私の 1 行のコマンドです ( bash)。

trap "jobs -p | xargs kill ; trap - INT" INT ; cmd1 & cmd2 & cmd3 &
# replace `cmds` with your command

これは、Ctrl+ をトラップしてCすべてのバックグラウンド ジョブを強制終了し、デフォルトの動作に戻します。

次に、すべてのコマンドをバックグラウンドで実行します。

Ctrl+を押すとC、それらすべてが強制終了され、トラップがデフォルトの動作に戻ります。

関連情報