バックグラウンドプロセスを実行するようにシェルスクリプトを設定しようとしています。 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
kill
コマンドは逆です。
多くのUNIXコマンドと同様に、マイナスで始まるオプションは、他の引数の前に最初に来る必要があります。
あなたが書くなら
kill -INT 0
-INT
をオプションとして認識し、SIGINT
を0
に送信します(0
は、現在のプロセスグループ内のすべてのプロセスを意味する特別な番号です)。
しかし、あなたが書くなら
kill 0 -INT
0
を確認し、これ以上オプションがないと判断したため、デフォルトでSIGTERM
を使用します。そして、あなたがしたのと同じように、現在のプロセスグループにthatを送信します
kill -TERM 0 -INT
(SIGTERM
を-INT
に送信しようとすると、構文エラーが発生しますが、最初にSIGTERM
を0
に送信し、それまで到達することはありません。)
したがって、メインスクリプトは、SIGTERM
とecho DONE
を実行する前にwait
を取得しています。
追加
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
残念ながら、バックグラウンドで開始されたコマンドは、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などの別の信号を使用します。
プロセスを強制終了してプロセスが終了するのを待つが、無期限ではない:
信号タイプごとに最大60秒待機します。
警告:この回答は、killシグナルをトラップしてディスパッチすることとはまったく関係がありません。
# close_app_sub GREP_STATEMENT SIGNAL DURATION_SEC
# GREP_STATEMENT must not match itself!
close_app_sub() {
APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
if [ ! -z "$APP_PID" ]; then
echo "App is open. Trying to close app (SIGNAL $2). Max $3sec."
kill $2 "$APP_PID"
WAIT_LOOP=0
while ps -p "$APP_PID" > /dev/null 2>&1; do
sleep 1
WAIT_LOOP=$((WAIT_LOOP+1))
if [ "$WAIT_LOOP" = "$3" ]; then
break
fi
done
fi
APP_PID=$(ps -x | grep "$1" | grep -oP '^\s*\K[0-9]+' --color=never)
if [ -z "$APP_PID" ]; then return 0; else return "$APP_PID"; fi
}
close_app() {
close_app_sub "$1" "-HUP" "60"
close_app_sub "$1" "-TERM" "60"
close_app_sub "$1" "-SIGINT" "60"
close_app_sub "$1" "-KILL" "60"
return $?
}
close_app "[f]irefox"
名前または引数によって殺すアプリを選択します。アプリ名の最初の文字は角かっこを使用して、grep自体が一致しないようにします。
いくつかの変更により、ps
ステートメントの代わりに、PIDまたはより単純なpidof process_name
を直接使用できます。
コードの詳細:最後のgrepは、末尾のスペースなしでPIDを取得することです。
バックグラウンドプロセスの管理が必要な場合は、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
だから私もこれをいじりました。 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、あなたの提案を見ただけで、それは非常に簡単になります!ありがとう!