2011-12-02 10 views
20

私はサーバがどのように負荷をかけて動作するかをテストするためのbashスクリプトを持っています。すべてのサブシェルプロセスを終了するにはどうしたらいいですか?

num=1 
if [ $# -gt 0 ]; then 
    num=$1 
fi 
for i in {1 .. $num}; do 
    (while true; do 
     { time curl --silent 'http://localhost'; } 2>&1 | grep real 
    done) & 
done   

wait 

私がCtrl-Cを押すと、メインプロセスは終了しますが、バックグラウンドループは動作し続けます。どうすればそれらをすべて終了させることができますか?または、並列実行可能な論理ループの設定可能な数を生成する良い方法がありますか?

答えて

33

は、ここで簡単なソリューションです - ちょうどあなたのスクリプトの先頭に次の行を追加します。

trap "kill 0" SIGINT 

0を殺すには、現在のプロセスグループ内のすべてのプロセスにシグナルを送信します。

+0

すごくきれいだとは言えますが、プロセスグループの管理方法は分かりません。それは、私が産んでいるすべてのバックグラウンドプロセスがスクリプトと同じプロセスグループに入っていることを保証するものですか? – ykaganovich

+0

はい、プロセスグループのデフォルトの動作です。プロセスのグループを明示的に変更してシステムコールを作成するコードを書かない限り、うまくいくでしょう。 –

+0

@RussellDavisこれはとても清潔で、とてもうまく動作します。私はこの作業をするためにマスタースクリプトから生成したすべてのシェルスクリプトにトラップを追加しなければなりませんでした。 – nograpes

2

job controlを使用する必要があります。残念ながら、少し複雑です。これらはあなたが実行していることを期待だけでバックグラウンドジョブであれば、あなたはこの1のようなコマンドを実行できます。

jobs \ 
    | perl -ne 'print "$1\n" if m/^\[(\d+)\][+-]? +Running/;' \ 
    | while read -r ; do kill %"$REPLY" ; done 

jobsは、すべてのアクティブなジョブ(実行中のジョブ、プラス最近終了または終了したジョブ)のリストを出力しますこのような形式で:

[1] Running     sleep 10 & 
[2] Running     sleep 10 & 
[3] Running     sleep 10 & 
[4] Running     sleep 10 & 
[5] Running     sleep 10 & 
[6] Running     sleep 10 & 
[7] Running     sleep 10 & 
[8] Running     sleep 10 & 
[9]- Running     sleep 10 & 
[10]+ Running     sleep 10 & 

(これらは私がfor i in {1..10} ; do sleep 10 & doneを実行して起動ジョブです。)

perl -ne ...は、実行中のジョブのジョブ番号を抽出するためにPerlを使ってくれています。必要に応じて、明らかに別のツールを使用することができます。 jobsの出力形式が異なる場合は、このスクリプトを変更する必要があります。上記の出力もCygwin上にあるので、あなたのものと非常によく似ています。

read -rは、標準入力から「生の」行を読み取り、変数$REPLYに保存します。 kill %"$REPLY"は、kill %1のようなものになり、ジョブ番号1を「キル」(割り込み信号を送信します)になります。と混同しないでください。プロセス番号1が抹消されます)。while read -r ; do kill %"$REPLY" ; doneは、 Perlスクリプトを実行し、それを強制終了します。

ところでブレース展開がパラメータ展開前を取り扱うので、何を持っていることはfor i in "{1" .. "$num}"に相当していることから、あなたのfor i in {1 .. $num}は、何を期待しないだろう。 (とにかく、中括弧拡張の中に空白を入れることはできません)残念ながら、私はきれいな代替案を知らない。私はあなたがfor i in $(bash -c "{1..$num}")のような何かをしなければならないと思う、または算術for -loopかそれ以外のものに切り替えなければならないと思う。

また、whileループをカッコで囲む必要はありません。 &は既にジョブをサブシェルで実行します。

+0

ヒントをありがとう、とところでヒントについては特に感謝します。私はバッシュの専門家ではないので、私はグーグルで書きます。 – ykaganovich

+0

よろしくお願いいたします。私はあなたが意味することを正確に知っています。私はBashのエキスパートでもなく、約1年前まで同じボートにいました.Bashリファレンスマニュアル(私の答えにリンクされています)が見つかりました。それは私の人生、あるいは少なくともそれのBashの部分を完全に変えました。 :-P – ruakh

0

私の最終的な解決策はここにあります。配列変数を使用してサブシェルプロセスIDを追跡し、Ctrl-Cシグナルをトラップしてそれらを削除します。

declare -a subs #array of subshell pids 

function kill_subs() { 
    for pid in ${subs[@]}; do 
     kill $pid 
    done 
    exit 0 
} 

num=1 if [ $# -gt 0 ]; then 
    num=$1 fi 

for ((i=0;i < $num; i++)); do 
    while true; do 
     { time curl --silent 'http://localhost'; } 2>&1 | grep real 
    done & 

    subs[$i]=$! #grab the pid of the subshell 
done 

trap kill_subs 1 2 15 

wait 
2

一つのサブシェルを殺すための方法ではなく、自己:

kill $(jobs -p) 
関連する問題