我正在寻找一种方法来清理混乱时,我的顶级脚本退出。
特别是如果我想使用set -e,我希望后台进程在脚本退出时结束。
我正在寻找一种方法来清理混乱时,我的顶级脚本退出。
特别是如果我想使用set -e,我希望后台进程在脚本退出时结束。
当前回答
只是为了多样性,我将发布https://stackoverflow.com/a/2173421/102484的变体,因为这个解决方案在我的环境中会导致消息“终止”:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM 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,但增加了退出代码
更新:https://stackoverflow.com/a/53714583/302079通过添加退出状态和清除功能改进了这一点。
trap "exit" INT TERM
trap "kill 0" EXIT
为什么转换INT和TERM退出?因为两者都应该在不进入无限循环的情况下触发kill 0。
为什么在EXIT时触发kill 0 ?因为正常的脚本退出也应该触发kill 0。
为什么要杀死0?因为嵌套的子shell也需要被杀死。这会把整个过程树都画下来。
如果在子shell中调用Jobs -p,则不能在所有shell中工作,除非它的输出被重定向到文件而不是管道中。(我想它最初只是用于交互使用的。)
下面这些怎么样:
trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...]
Debian的dash shell需要调用“jobs”,如果缺少当前作业(“%%”),则该shell无法更新。
trap 'kill $(jobs -p)'退出
我只会对Johannes的答案做一些小改动,并使用jobs -pr将kill限制为正在运行的进程,并在列表中添加更多的信号:
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT
我终于找到了一个解决方案,在所有情况下都可以递归地终止所有下降,不管它们是作业还是子流程。这里的其他解决方案似乎都失败了,比如:
while ! ffmpeg ....
do
sleep 1
done
在我的情况下,ffmpeg将在父脚本退出后继续运行。
我在这里找到了一个递归地获得所有子进程的pid的解决方案,并在陷阱处理程序中使用它:
cleanup() {
# kill all processes whose parent is this process
kill $(pidtree $$ | tac)
}
pidtree() (
[ -n "$ZSH_VERSION" ] && setopt shwordsplit
declare -A CHILDS
while read P PP;do
CHILDS[$PP]+=" $P"
done < <(ps -e -o pid= -o ppid=)
walk() {
echo $1
for i in ${CHILDS[$1]};do
walk $i
done
}
for i in "$@";do
walk $i
done
)
trap cleanup EXIT
上面放在bash脚本开头的代码成功地杀死了所有子进程。请注意,pidtree是用$$调用的,$$是正在退出的bash脚本的PID,并且PID列表(每行一个)使用tac反转,以尝试确保父进程只在子进程之后被杀死,以避免在循环中可能出现的竞争条件,例如我给出的例子。