Answers:
可以清理一些乱七八糟的东西trap
。它可以提供在特定信号到达时执行的工作清单:
trap "echo hello" SIGINT
但如果shell退出,也可以用来执行某些操作:
trap "killall background" EXIT
它是内置的,因此help trap
将为您提供信息(适用于bash)。如果您只想杀死后台工作,则可以
trap 'kill $(jobs -p)' EXIT
当心使用single '
,以防止shell $()
立即替换。
kill $(jobs -p)
在破折号中不起作用,因为它在子shell中执行命令替换(请参见man破折号中的命令替换)
killall background
认为是一个占位符?background
不在手册页中……
这对我有用(感谢评论者的改进):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
4.3.30(1)-release
在OSX 上遇到了这个问题,在Ubuntu上也得到了证实。不过,这里有一个让人讨厌的地方:)
-$$
。评估为'-<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
含义/含义?
@tokland的答案中trap 'kill 0' SIGINT SIGTERM EXIT
描述的解决方案非常好,但是最新的Bash 在使用时会出现段错误。这是因为Bash从4.3版开始,允许陷阱递归,在这种情况下,该递归变得无限:
SIGINT
或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
它将在第二个stop
调用期间被释放。
trap - $1 && kill -s $1 0
应该会更好。我将测试并更新此答案。谢谢你的好主意!:)
trap - $1 && kill -s $1 0
不会工作,因为我们不能用杀死EXIT
。但是做de-trap确实足够了TERM
,因为kill
默认情况下发送此信号。
EXIT
,trap
信号处理程序始终只执行一次。
为了安全起见,我发现最好定义一个清理函数并从trap中调用它:
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
是空的-我以“陷阱”(双关语)为结尾。
[ -n "$(jobs -pr)" ]
不适用于我的bash。我使用GNU bash版本4.2.46(2)-发行版(x86_64-redhat-linux-gnu)。“ kill:用法”消息不断弹出。
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一样,但是添加了退出代码
当我注意到如果我正在运行前台流程时不会触发该操作时,我对@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的变体,因为该解决方案会导致我的环境中出现“已终止”消息:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT