使用 bash 的作业控制捕获 EXIT 并杀死

使用 bash 的作业控制捕获 EXIT 并杀死

我有一个运行多个程序的 bash 脚本

#!/bin/sh
python program1.py &
python program2.py &
other programs ... &
lastProgram

我运行它作为./myscript.sh

当我按Ctrl+C关闭它时lastProgram,它退出,所有其他程序继续在后台运行。问题是其他程序需要终止。

哪种方法可以正确处理从脚本启动的所有程序的关闭?

答案1

收集进程 ID,退出时终止后台进程。

#!/bin/bash
killbg() {
        for p in "${pids[@]}" ; do
                kill "$p";
        done
}
trap killbg EXIT
pids=()
background job 1 & 
pids+=($!)
background job 2... & 
pids+=($!)
foreground job

EXIT无论原因如何,当 shell 退出时,捕获都会运行该函数。您可以将其更改为trap killbg SIGINT仅在^C.

这不会检查后台进程之一在脚本尝试启动它们之前是否已退出。如果这样做,您可能会出错,或者更糟糕的是,拍摄错误的流程。


或者通过工作 ID 杀死他们。让我们阅读 的输出,jobs找出哪些仍然处于活动状态。

#!/bin/bash 
killjobs() {
    for x in $(jobs | awk -F '[][]' '{print $2}' ) ; do 
        kill %$x
    done
}
trap killjobs EXIT
sleep 999 &
sleep 1 &
sleep 999 &
sleep 30

如果您运行生成其他进程的后台进程(例如子 shell: (sleep 1234 ; echo foo) &),则需要使用set -m(“监视模式”)启用作业控制才能使其正常工作。否则仅终止引导进程。

答案2

我刚刚读到类似的问题,关于收集 PID,然后在脚本末尾杀死它们 -问题是一个已完成进程的 PID可以在新流程中回收并重新使用在你的脚本完成之前,然后你可以杀死一个新的(随机)进程

使用 bash 的作业控制捕获 EXIT 并杀死

您可以使用 bash 的作业控制仅终止在脚本中使用陷阱和 %n jobspec 启动的进程,计算可以运行的最大作业数(本例中只有 3 个):

#!/bin/bash
#trap 'kill %1 %2 %3' 0     # 0 or EXIT are equivalent
#trap 'kill %1 %2 %3' EXIT  # or use {1..n} as below
trap 'kill %{1..3}' EXIT
sleep 33 &
sleep 33 &
sleep 33 &
echo processes are running, ctrl-c the next sleep
sleep 66
echo this line will never be executed

对已经完成的不存在的作业规范的任何额外“杀死”只会导致错误消息,它不会杀死任何其他新/随机进程。


杀死脚本的完整进程组

这是一种稍微不同的方式来终止脚本的完整进程组。但是,如果您的脚本/shell 的作业控制未设置,那么它可以从它的父级继承它的 PPID...但是如果没有作业控制,上面的方法也不起作用。

不同之处在于 trap 的kill 命令使用 bash 的 PID,因为它成为新进程的 PGID:

trap 'kill -- -$$' EXIT

这个相关的问题或者在这里,Johannes“钓鱼”Ziemke 捕获 SIGINT 和 SIGTERM 并用于setsid在新的进程组中杀死整个进程组,这样我们就不会冒自杀的风险”。

答案3

如果您想终止所有后台进程(如果它们在lastProgram执行完成之前未完成),则需要存储所有 pid:

python program1.py &
p1pid=$!
python program2.py &
p2pid=$!
other programs ... &
p3pid=$!
lastProgram

kill $p1pid $p2pid $p3pid

如果您只是想等到所有后台进程执行完毕后再退出脚本,则可以使用该wait命令。

python program1.py &
python program2.py &
other programs ... &
lastProgram
wait

答案4

这是我的一行命令(在bash):

trap "jobs -p | xargs kill ; trap - INT" INT ; cmd1 & cmd2 & cmd3 &
# replace `cmds` with your command

它的作用是捕获Ctrl+C来杀死所有后台作业并恢复到其默认行为。

然后,它在后台运行所有命令。

当你点击Ctrl+时C,它会杀死所有陷阱并将陷阱重置为其默认行为。

相关内容