Ctrlcバックグラウンドプロセスを実行してシェルスクリプトを使用するときは、サブプロセスを終了してから終了するようにシェルスクリプトを設定しようとしています。
私が考えることができる最善はこれです。kill 0 -INT
待機が発生する前にスクリプトも終了するように見えるため、シェルスクリプトは子プロセスが完了する前に終了します。
このシェルスクリプトが送信された後に子が死ぬのを待つ方法についてのアイデアはありますかINT
?
#!/bin/bash
trap 'killall' INT
killall() {
echo "**** Shutting down... ****"
kill 0 -INT
wait # Why doesn't this wait??
echo DONE
}
process1 &
process2 &
process3 &
cat # wait forever
答え1
あなたのkill
命令は逆さまです。
多くのUNIXコマンドと同様に、マイナス記号で始まるオプションは他のパラメータの前に表示する必要があります。
書くなら
kill -INT 0
-INT
オプションとして処理されSIGINT
(現在のプロセスグループ内のすべてのプロセスを表す特殊番号)0
に送信されます。0
しかし、書いてみると
kill 0 -INT
を表示し、0
オプションがないと判断してSIGTERM
デフォルト値を使用します。そして送るそれあなたがそうであったように、現在のプロセスグループに
kill -TERM 0 -INT
SIGTERM
(また、送信しようとする-INT
と構文エラーが発生しますが、最初に送信SIGTERM
して0
それ以上到達しません。)
したがって、デフォルトのスクリプトをSIGTERM
実行するwait
前とecho DONE
。
次へ追加
trap 'echo got SIGTERM' TERM
トップ、すぐ後ろに
trap 'killall' INT
それを証明するために再実行しました。
Stephane Chazelasが指摘したように、背景がある子供は基本的に無視されprocess1
ます。SIGINT
とにかく過ごす方がSIGTERM
合理的だと思います。
結局、kill -process group
子どもたちに先にあげると言うことはできないようです。ドアを閉めるときは、信号を無視することをお勧めします。
だからこれを試してみてください:
#!/bin/bash
trap 'killall' INT
killall() {
trap '' INT TERM # ignore INT and TERM while shutting down
echo "**** Shutting down... ****" # added double quotes
kill -TERM 0 # fixed order, send TERM not INT
wait
echo DONE
}
./process1 &
./process2 &
./process3 &
cat # wait forever
答え2
残念ながら、バックグラウンドで開始されたコマンドは、SIGINTを無視するようにシェルによって設定され、悪いことは機能しないことtrap
です。
(trap - INT; exec process1) &
(trap - INT; exec process2) &
trap '' INT
wait
Ctrl-Cを押すと、process1とprocess2が同じプロセスグループ(つまり、端末のフォアグラウンドプロセスグループ)に属するため、SIGINTを受け取るためです。
上記のコードは、この点でPOSIXと互換性のないpdkshとzshで使用されます。
他のシェルの場合は、次のような他の方法を使用してSIGINTのデフォルトハンドラを復元する必要があります。
perl -e '$SIG{INT}=DEFAULT; exec "process1"' &
または、SIGTERMなどの他の信号を使用してください。
答え3
いくつかのバックグラウンドプロセスを管理したい場合は、bashジョブ制御機能を試してみてはいかがでしょうか?
$ gedit &
[1] 2581
$ emacs &
[2] 2594
$ jobs
[1]- Running gedit &
[2]+ Running emacs &
$ jobs -p
2581
2594
$ kill -2 `jobs -p`
$ jobs
[1]- Interrupt gedit
[2]+ Done emacs
答え4
だからそれも修正しました。 Bashでは、関数を定義し、それを使用して興味深い操作を実行できます。私と同僚はterminator
これを一括実行に使用します。出力を表示するには、通常、複数のエミッタウィンドウを作成します(tmux
タブほどエレガントではありませんが、GUIに似たインターフェイスを使用できます。ハハ)。
私が思いついた設定と実行できる例は次のとおりです。
#!/bin/bash
custom()
{
terun 'something cool' 'yes'
run 'xclock'
terun 'echoes' 'echo Hello; echo Goodbye; read'
func()
{
local i=0
while :
do
echo "Iter $i"
let i+=1
sleep 0.25
done
}
export -f func
terun 'custom func' 'func'
}
# [ Setup ]
run()
{
"$@" &
# Give process some time to start up so windows are sequential
sleep 0.05
}
terun()
{
run terminator -T "$1" -e "$2"
}
finish()
{
procs="$(jobs -p)"
echo "Kill: $procs"
# Ignore process that are already dead
kill $procs 2> /dev/null
}
trap 'finish' 2
custom
echo 'Press <Ctrl+C> to kill...'
wait
実行する
$ ./program_spawner.sh
Press <Ctrl+C> to kill...
^CKill: 9835
9840
9844
9850
$
編集する @Maximさん、ちょうどあなたの提案を見て、はるかに簡単になりました!ありがとうございます!