我正在寻找一种方法来清理混乱时,我的顶级脚本退出。
特别是如果我想使用set -e,我希望后台进程在脚本退出时结束。
我正在寻找一种方法来清理混乱时,我的顶级脚本退出。
特别是如果我想使用set -e,我希望后台进程在脚本退出时结束。
当前回答
通用解决方案,也在sh中工作(工作不输出任何东西到stdout):
trap "pkill -P $$" EXIT INT
其他回答
另一种选择是让脚本将自己设置为进程组的领导者,并在退出时在进程组上捕获killpg。
只是为了多样性,我将发布https://stackoverflow.com/a/2173421/102484的变体,因为这个解决方案在我的环境中会导致消息“终止”:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT
为了清理一些混乱,陷阱可以使用。它可以提供特定信号到达时执行的内容列表:
trap "echo hello" SIGINT
但如果shell退出,也可以用来执行一些东西:
trap "killall background" EXIT
它是内置的,所以帮助陷阱会给你信息(适用于bash)。如果你只想消灭后台工作,你可以做到
trap 'kill $(jobs -p)' EXIT
注意使用单个',以防止shell立即替换$()。
通用解决方案,也在sh中工作(工作不输出任何东西到stdout):
trap "pkill -P $$" EXIT INT
我终于找到了一个解决方案,在所有情况下都可以递归地终止所有下降,不管它们是作业还是子流程。这里的其他解决方案似乎都失败了,比如:
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反转,以尝试确保父进程只在子进程之后被杀死,以避免在循环中可能出现的竞争条件,例如我给出的例子。