如何终止所有子shell进程?

yka*_*ich 29 bash cygwin

我有一个bash脚本来测试服务器在负载下的执行情况.

num=1
if [ $# -gt 0 ]; then
    num=$1
fi
for i in {1 .. $num}; do
    (while true; do
        { time curl --silent 'http://localhost'; } 2>&1 | grep real
    done) &
done        

wait
Run Code Online (Sandbox Code Playgroud)

当我按下Ctrl-C时,主进程退出,但后台循环继续运行.如何让它们全部退出?或者是否有更好的方法来生成可并行执行的可配置数量的逻辑循环?

Rus*_*vis 47

这是一个更简单的解决方案 - 只需在脚本顶部添加以下行:

trap "kill 0" SIGINT
Run Code Online (Sandbox Code Playgroud)

Killing 0将信号发送到当前进程组中的所有进程.

  • 有什么特殊原因不像这个答案那样捕获 SIGTERM 和 EXIT 吗?http://stackoverflow.com/a/2173421/179583 (3认同)
  • 我很好奇你是怎么发现这个的。我在 kill 的手册页中找不到它。 (2认同)

Cra*_*een 5

一种杀死子外壳但不是自我的方法:

kill $(jobs -p)
Run Code Online (Sandbox Code Playgroud)


Har*_*ikk 5

答案有点晚了,但对我来说,解决方案就像kill 0kill $(jobs -p)走得太远(杀死所有子进程)。

如果您只是想确保一个特定的子进程(及其自己的子进程)得到整理,那么更好的解决方案是使用子进程的 PID 按进程组 (PGID) 进行终止,如下所示:

set -m
./some_child_script.sh &
some_pid=$!

kill -- -${some_pid}
Run Code Online (Sandbox Code Playgroud)

首先,该set -m命令将启用作业管理(如果尚未启用),这很重要,否则所有命令、子 shell 等都将被分配到与父脚本相同的进程组(与运行命令时不同)在终端中手动执行),并且kill只会给出“没有这样的进程”错误。在运行您希望作为一个组管理的后台命令之前需要调用它(或者如果您有多个,则在脚本启动时调用它)。

其次,请注意 的参数kill是否定的,这表明您想要杀死整个进程组。默认情况下,进程组 ID 与组中的第一个命令相同,因此我们只需在使用 获取的 PID 前面添加一个减号即可获得它$!。如果您需要在更复杂的情况下获取进程组 ID,则需要使用ps -o pgid= ${some_pid},然后添加减号。

最后,请注意 options 的显式结尾的使用--,这很重要,否则进程组参数将被视为选项(信号号),并且kill会抱怨它没有足够的参数。仅当进程组参数是您希望终止的第一个进程组参数时才需要此参数。

这是后台超时过程的简化示例,以及如何尽可能清理:

#!/bin/bash
# Use the overkill method in case we're terminated ourselves
trap 'kill $(jobs -p | xargs)' SIGINT SIGHUP SIGTERM EXIT

# Setup a simple timeout command (an echo)
set -m
{ sleep 3600; echo "Operation took longer than an hour"; } &
timeout_pid=$!

# Run our actual operation here
do_something

# Cancel our timeout
kill -- -${timeout_pid} >/dev/null 2>&1
wait -- -${timeout_pid} >/dev/null 2>&1
printf '' 2>&1
Run Code Online (Sandbox Code Playgroud)

这应该可以在所有合理的情况下干净地处理取消这个简单的超时;唯一无法处理的情况是脚本立即终止 ( kill -9),因为它没有机会进行清理。

我还添加了一个wait,后跟一个 no-op ( printf ''),这是为了抑制命令可能引起的“终止”消息kill,这有点像黑客,但根据我的经验,它足够可靠。