当我的顶级脚本退出时,我正在寻找一种清理混乱的方法。
特别是如果我想使用set -e
,我希望当脚本退出时后台进程会死掉。
答案 0 :(得分:160)
要清理一些乱七八糟的东西,可以使用trap
。它可以提供特定信号到达时执行的内容列表:
trap "echo hello" SIGINT
但如果shell退出,也可用于执行某些操作:
trap "killall background" EXIT
它是内置的,因此help trap
会为您提供信息(与bash一起使用)。如果您只想杀死后台作业,可以执行
trap 'kill $(jobs -p)' EXIT
注意使用单'
,以防止shell立即替换$()
。
答案 1 :(得分:142)
这对我有用(感谢评论者的改进):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
答案 2 :(得分:99)
更新: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
?因为嵌套的子壳也需要被杀死。这将取消the whole process tree。
答案 3 :(得分:21)
陷阱'kill $(jobs -p)'退出
我只会对Johannes的回答进行微小的更改,并使用jobs -pr将kill限制为正在运行的进程并向列表中添加更多信号:
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT
答案 4 :(得分:12)
@tokland's answer中描述的trap 'kill 0' SIGINT SIGTERM EXIT
解决方案非常好,但在使用时最新Bash crashes with a segmantation fault。那是因为从v.4.3开始,Bash允许陷阱递归,在这种情况下它变为无限:
SIGINT
或SIGTERM
或EXIT
; kill 0
,将SIGTERM
发送给组中的所有进程,包括shell本身; 这可以通过手动取消注册陷阱来解决:
trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT
更奇特的方式,允许打印收到的信号并避免“终止:”消息:
#!/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
函数以避免去除不必要的信号并从输出中隐藏“已终止:”消息。感谢Trevor Boyd Smith提出建议!
答案 5 :(得分:7)
为了安全起见,我发现最好定义一个清理函数并从陷阱中调用它:
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)' [...]
,就会假定 是在发出陷阱条件时运行的后台作业。当没有工作时,人们将看到以下(或类似)消息:
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
因为jobs -pr
是空的 - 我以'陷阱'结束了(双关语)。
答案 6 :(得分:2)
在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
请注意,工作不包括大孩子流程。
答案 7 :(得分:1)
另一种选择是让脚本将自己设置为流程组负责人,并在退出时在流程组中捕获killpg。
答案 8 :(得分:1)
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,但添加了退出代码
答案 9 :(得分:0)
因此脚本加载脚本。运行脚本完成后立即执行的killall
(或您的操作系统上可用的任何内容)命令。
答案 10 :(得分:0)
jobs -p在所有shell中都不起作用,可能除非将其输出重定向到文件而不是管道。 (我认为它最初仅用于交互式使用。)
以下内容如何:
trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...]
Debian的破折号外壳需要调用“作业”,如果缺少,则无法更新当前作业(“%%”)。
答案 11 :(得分:0)
当我注意到如果我正在运行前台进程时trap
没有触发,我对@ tokland的回答以及来自http://veithen.github.io/2014/11/16/sigterm-propagation.html的知识进行了调整(不以&
为背景):
#!/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
答案 12 :(得分:0)
仅出于多样性,我将发布https://stackoverflow.com/a/2173421/102484的变体,因为该解决方案会在我的环境中导致消息“终止”:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT