回答:
いくつかの混乱をクリーンアップtrap
するために使用できます。特定のシグナルが到着したときに実行されるもののリストを提供できます。
trap "echo hello" SIGINT
シェルが終了した場合に何かを実行するために使用することもできます:
trap "killall background" EXIT
これは組み込みなので、help trap
情報を提供します(bashで動作します)。バックグラウンドジョブのみを殺したい場合は、
trap 'kill $(jobs -p)' EXIT
'
シェルが$()
即座に置換されないように、singleを使用することに注意してください。
kill $(jobs -p)
サブシェルでコマンド置換を実行するため、ダッシュでは機能しません(ダッシュのコマンド置換を参照)
killall background
プレースホルダことになって?background
はマニュアルページにはありません...
これは私にとってはうまくいきます(コメントのおかげで改善されました):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
4.3.30(1)-release
OSXでこれに遭遇し、Ubuntuでも確認されています。しかし、obvoius wokaroundがあります:)
-$$
。'-<PID> `と評価され-1234
ます。killマンページ//組み込みマンページでは、先頭のダッシュは送信するシグナルを指定します。ただし、おそらくそれをブロックしますが、それ以外の場合、先頭のダッシュは文書化されていません。何か助けは?
man 2 kill
。これは、PIDが負の場合、信号が、指定されたID(en.wikipedia.org/wiki/Process_group)を持つプロセスグループ内のすべてのプロセスに送信されることを説明します。これがman 1 kill
またはman bash
で言及されておらず、ドキュメントのバグと見なされる可能性があることは混乱を招きます。
更新:https : //stackoverflow.com/a/53714583/302079は、終了ステータスとクリーンアップ機能を追加することでこれを改善しています。
trap "exit" INT TERM
trap "kill 0" EXIT
なぜ変換INT
しTERM
て終了するのですか?どちらもkill 0
無限ループに入ることなくをトリガーする必要があるためです。
なぜトリガーkill 0
するのEXIT
ですか?通常のスクリプト出口がトリガーする必要があるためkill 0
もあるためです。
なんでkill 0
?ネストされたサブシェルも削除する必要があるためです。これにより、プロセスツリー全体が削除されます。
kill 0
意味し、何をしているのかについてもっと詳しく説明してもらえますか?
trap 'kill 0' SIGINT SIGTERM EXIT
記載された解決策@ toklandの答えは本当に素晴らしいですが、最新のバッシュはsegmantationフォールトでクラッシュし、それを使用した場合。これは、バージョン4.3以降のBashがトラップの再帰を許可するためです。この場合、トラップの再帰は無限になります。
SIGINT
or SIGTERM
またはEXIT
;kill 0
送信SIGTERM
されます。これは手動でトラップを登録解除することで回避できます。
trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT
より手の込んだ方法では、受信した信号を出力し、「Terminated:」メッセージを回避できます。
#!/usr/bin/env bash
trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678
local func="$1"; shift
for sig in "$@"; do
trap "$func $sig" "$sig"
done
}
stop() {
trap - SIGINT EXIT
printf '\n%s\n' "recieved $1, killing children"
kill -s SIGINT 0
}
trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP
{ i=0; while (( ++i )); do sleep 0.5 && echo "a: $i"; done } &
{ i=0; while (( ++i )); do sleep 0.6 && echo "b: $i"; done } &
while true; do read; done
UPD:最小限の例を追加。stop
不要な信号のデトラップを回避し、「Terminated:」メッセージを出力から隠す機能が改善されました。提案をしてくれたTrevor Boyd Smithに感謝します!
stop()
あなたのシグナル番号として最初の引数を提供しますが、あなたは信号が登録解除されているものをハードコーディング。登録解除される信号をハードコードするのではなく、最初の引数を使用してstop()
関数で登録解除できます(そうすると、他の再帰的信号(ハードコードされた3以外)が停止する可能性があります)。
SIGINT
がありkill 0
ますがSIGTERM
、を送信すると、再びトラップされます。ただし、SIGTERM
2番目のstop
呼び出し中にトラップが除去されるため、これは無限再帰を生成しません。
trap - $1 && kill -s $1 0
もっとうまくいくはずです。この回答をテストして更新します。素敵なアイデアをありがとう!:)
trap - $1 && kill -s $1 0
私たちはで殺せないので、あまり機能しませんEXIT
。しかし、デフォルトでこのシグナルを送信するTERM
ため、実際にはdo de-trap で十分kill
です。
EXIT
た。trap
シグナルハンドラは常に1回だけ実行されます。
安全のために、クリーンアップ関数を定義してトラップから呼び出すほうがよいと思います。
cleanup() {
local pids=$(jobs -pr)
[ -n "$pids" ] && kill $pids
}
trap "cleanup" INT QUIT TERM EXIT [...]
または関数を完全に回避する:
trap '[ -n "$(jobs -pr)" ] && kill $(jobs -pr)' INT QUIT TERM EXIT [...]
どうして?単にtrap 'kill $(jobs -pr)' [...]
1 を使用することで、トラップ条件が通知されたときにバックグラウンドジョブが実行されると想定しているためです。ジョブがない場合、次の(または同様の)メッセージが表示されます。
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
jobs -pr
空だからです-私はその「罠」(意図されたしゃれ)で終わりました。
[ -n "$(jobs -pr)" ]
は私のbashでは機能しません。私はGNU bash、バージョン4.2.46(2)-release(x86_64-redhat-linux-gnu)を使用しています。「kill:usage」メッセージがポップアップし続けます。
jobs -pr
バックグラウンドプロセスの子のPIDを返さないという事実に関係しているのではないかと思います。プロセスツリー全体を破壊するのではなく、ルートを削除するだけです。
Linux、BSD、MacOS Xで動作する素晴らしいバージョン。最初にSIGTERMを送信しようとし、成功しない場合は、10秒後にプロセスを強制終了します。
KillJobs() {
for job in $(jobs -p); do
kill -s SIGTERM $job > /dev/null 2>&1 || (sleep 10 && kill -9 $job > /dev/null 2>&1 &)
done
}
TrapQuit() {
# Whatever you need to clean here
KillJobs
}
trap TrapQuit EXIT
ジョブには孫プロセスは含まれないことに注意してください。
function cleanup_func {
sleep 0.5
echo cleanup
}
trap "exit \$exit_code" INT TERM
trap "exit_code=\$?; cleanup_func; kill 0" EXIT
# exit 1
# exit 0
https://stackoverflow.com/a/22644006/10082476に似ていますが、exit-codeが追加されています
フォアグラウンドプロセスを実行している場合にトリガーされないことに気付いたとき、@ toklandの回答とhttp://veithen.github.io/2014/11/16/sigterm-propagation.htmlの知識を組み合わせて変更しましたtrap
(でバックグラウンド化されていません&
):
#!/bin/bash
# killable-shell.sh: Kills itself and all children (the whole process group) when killed.
# Adapted from http://stackoverflow.com/a/2173421 and http://veithen.github.io/2014/11/16/sigterm-propagation.html
# Note: Does not work (and cannot work) when the shell itself is killed with SIGKILL, for then the trap is not triggered.
trap "trap - SIGTERM && echo 'Caught SIGTERM, sending SIGTERM to process group' && kill -- -$$" SIGINT SIGTERM EXIT
echo $@
"$@" &
PID=$!
wait $PID
trap - SIGINT SIGTERM EXIT
wait $PID
それが機能する例:
$ bash killable-shell.sh sleep 100
sleep 100
^Z
[1] + 31568 suspended bash killable-shell.sh sleep 100
$ ps aux | grep "sleep"
niklas 31568 0.0 0.0 19640 1440 pts/18 T 01:30 0:00 bash killable-shell.sh sleep 100
niklas 31569 0.0 0.0 14404 616 pts/18 T 01:30 0:00 sleep 100
niklas 31605 0.0 0.0 18956 936 pts/18 S+ 01:30 0:00 grep --color=auto sleep
$ bg
[1] + 31568 continued bash killable-shell.sh sleep 100
$ kill 31568
Caught SIGTERM, sending SIGTERM to process group
[1] + 31568 terminated bash killable-shell.sh sleep 100
$ ps aux | grep "sleep"
niklas 31717 0.0 0.0 18956 936 pts/18 S+ 01:31 0:00 grep --color=auto sleep
多様性のために、私はhttps://stackoverflow.com/a/2173421/102484のバリエーションを投稿します。その解決策は私の環境で「Terminated」というメッセージにつながるためです。
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT