2013-08-16 14 views
9

I持つバックグラウンドプロセスとして無限のコマンドを実行しますbashの:bashシャットダウンフック;メインプロセスが殺されたときや、すべてのバックグラウンド・プロセスを殺す

#!/bin/bash 

function xyz() { 
    # some awk command 
} 

endlesscommand "param 1" | xyz & # async 
pids=$! 
endlesscommand "param 2" | xyz & # async 
pids="$pids "$! 
endlesscommand "param 3" | xyz # sync so the script doesn't leave 

このスクリプトを停止する唯一の方法はある(なければなりません)はCtrl-Cまたは殺すとその場合、$ pids変数にリストされているすべてのバックグラウンドプロセスを強制終了する必要があります。 どうすればいいですか?

私のようなものだろうメインプロセスにkillシグナルをキャッチし、それが起こるとき(シャットダウンフック)関数を実行することが可能であった場合は、次の

for $pid in $pids; do kill $pid; done; 

をしかし、私は見つけることができませんどのように次の記事からこれを行う...


SOLUTION

#!/bin/bash 

function xyz() { 
# some awk command 
} 

trap 'jobs -p | xargs kill' EXIT # part of the solution 

endlesscommand "param 1" | xyz & # async 
endlesscommand "param 2" | xyz & # async 
endlesscommand "param 3" | xyz & # don't sync, but wait: 

# other part of the solution: 
while pgrep -P "$BASHPID" > /dev/null; do 
    wait 
done 
+0

トラップを使用する必要があります... – devnull

+0

子プロセスの明示的なリストを維持する必要はありません。 'jobs -p'を呼び出して、まだ実行中の子プロセスIDを取得することができます。 – chepner

答えて

10

はここpidsを追跡するためにあなたを必要としないトラップです:

trap 'jobs -p | xargs kill' EXIT 

はEDIT:@Barmarがあれば尋ねこれはジョブの制御が通常利用できない非ソーススクリプト内で動作します。します。私たちは3グループリーダーのPIDを参照して実行すると

$ cat no-job-control 
#! /bin/bash 

set -e -o pipefail 

# Prove job control is off 
if suspend 
then 
    echo suspended 
else 
    echo suspension failed, job control must be off 
fi 

echo 

# Set up the trap 
trap 'jobs -p | xargs kill' EXIT 

# Make some work 
(echo '=> Starting 0'; sleep 5; echo '=> Finishing 0') & 
(echo '=> Starting 1'; sleep 5; echo '=> Finishing 1') & 
(echo '=> Starting 2'; sleep 5; echo '=> Finishing 2') & 

echo "What's in jobs -p?" 
echo 

jobs -p 

echo 
echo "Ok, exiting now" 
echo 

し、それらが殺さ参照してください:このスクリプトを考えてみましょう

$ ./no-job-control 
./no-job-control: line 6: suspend: cannot suspend: no job control 
suspension failed, job control must be off 

=> Starting 0 
What's in jobs -p? 
=> Starting 1 

54098 
54099 
54100 

Ok, exiting now 

=> Starting 2 
./no-job-control: line 31: 54098 Terminated: 15   (echo '=> Starting 0'; sleep 5; echo '=> Finishing 0') 
./no-job-control: line 31: 54099 Terminated: 15   (echo '=> Starting 1'; sleep 5; echo '=> Finishing 1') 
./no-job-control: line 31: 54100 Terminated: 15   (echo '=> Starting 2'; sleep 5; echo '=> Finishing 2') 

我々は代わりにtrapラインとは、再実行をコメントアウトした場合、 3つのジョブが死ぬことはなく、実際には数秒後に最終的なメッセージを出力します。返されたプロンプトが最終出力とインターリーブされていることに注目してください。

$ ./no-job-control 
./no-job-control: line 6: suspend: cannot suspend: no job control 
suspension failed, job control must be off 

=> Starting 0 
What's in jobs -p? 

54110 
54111 
54112 
=> Starting 1 

Ok, exiting now 

=> Starting 2 
$ => Finishing 0 
=> Finishing 2 
=> Finishing 1 
+1

パイプであっても、素晴らしいことです! :) それはさらに短くてもよい: trap 'jobs -p | xargs kill 'EXIT – fabien

+0

ジョブ制御が有効になっていないにもかかわらず、 'jobs -p'はスクリプト内で実際に動作しますか? – Barmar

+0

@Barmar編集を参照してください。 – phs

0
kill `ps axl | grep "endlesscommand" | awk '{printf $4" "}'` 

は、これが影響している親プロセスを探します "endlesscommand"

+0

'kill -9'は軽く使わないでください。単純な '' kill''で十分です。また、これは 'endlesscommand'を実行しているすべてのプロセスを強制終了します。 – chepner

+0

@chepner生成されたプロセスには間違っています... OPは関係するすべてのプロセスを終了させたいので、なぜ 'kill -9'が安全でないのか理解できません。 – iamauser

+0

'kill -9'は、終了したプロセスに、ファイルのクローズ、バッファのフラッシュなどの機会を与えません。 – chepner

1

このようにメインプロセスで作成されたすべてのプロセスをpgrepと関数を使って終了することができます。これにより、直接的な子プロセスだけでなく、その下に作成されたプロセスも強制終了されます。

#!/bin/bash 

function killchildren { 
    local LIST=() IFS=$'\n' A 
    read -a LIST -d '' < <(exec pgrep -P "$1") 
    local A SIGNAL="${2:-SIGTERM}" 
    for A in "${LIST[@]}"; do 
     killchildren_ "$A" "$SIGNAL" 
    done 
} 

function killchildren_ { 
    local LIST=() 
    read -a LIST -d '' < <(exec pgrep -P "$1") 
    kill -s "$2" "$1" 
    if [[ ${#LIST[@]} -gt 0 ]]; then 
     local A 
     for A in "${LIST[@]}"; do 
      killchildren_ "$A" "$2" 
     done 
    fi 
} 

trap 'killchildren "$BASHPID"' EXIT 

endlesscommand "param 1" & 
endlesscommand "param 2" & 
endlesscommand "param 3" & 

while pgrep -P "$BASHPID" >/dev/null; do 
    wait 
done 

あなたの元のコードについては、それだけで配列を使用する方が良いだろう、とあなたはまた、forループを使用する必要はありません。

#!/bin/bash 

trap 'kill "${pids[@]}"' EXIT 

pids=() 
endlesscommand "param 1" & # async 
pids+=("$!") 
endlesscommand "param 2" & # async 
pids+=("$!") 
endlesscommand "param 3" & # syncing this is not a good idea since if the main process would end along with it if it ends earlier. 
pids+=("$!") 

while pgrep -P "$BASHPID" >/dev/null; do 
    wait 
done 

オリジナル関数参照:http://www.linuxquestions.org/questions/blog/konsolebox-210384/bash-functions-to-list-and-kill-or-send-signals-to-process-trees-34624/

+0

私はこの最後の解決策を試して、それは動作します...しかし、私のendlesscommandがいくつかの他のプログラム/機能(私の質問を更新)とパイプされていない場合。 最後に、私はあなたのpgrepループ&phsソリューションを使用しており、とてもうまく動作します。 :) – fabien

関連する問題