我有一个运行多个程序的 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,它会杀死所有陷阱并将陷阱重置为其默认行为。