在一组中启动后台进程,然后将其全部杀死

在一组中启动后台进程,然后将其全部杀死

有一天,我尝试编写一个脚本来杀死一个PID及其所有子进程,但在花了一些时间之后,我认为它不值得信赖,因为有时一些子进程最终会得到一个PPID1

现在,我正在寻找的是如何在后台运行以下函数,并且使该函数和curl所有函数都在同一个中团体,然后在卡住时杀死该脚本,杀死留在后台的所有子脚本:

download(){
    until curl -s -S -L -A Mozilla/5.0 -m 300 "$@"; do
        echo Retrying in 5 seconds... >&2
        sleep 5
    done
}

for url in foo.com bar.com baz.com; do
    download $url >$url &
done
wait

我知道不需要这个函数download,但我将其包含在此处,因为这是我在此脚本中多次使用的函数。另外,我给出的最大时间限制为300curl,但根据一些网络错误,它也会卡住。

答案1

通常,如果您从交互式 shell 调用脚本,它将被放入一个新的进程组(又名job)中,因此,如果您Ctrl-C使用该脚本,则由该脚本启动的所有进程都将收到 SIGINT。

如果您也在后台启动它(仍然从交互式 shell),它也将在其自己的进程组中启动。

您可以通过以下方式了解进程组:

ps -j

j是指job control在进程组中运行命令行以便能够管理它们的 shell 的行为(前台/后台/kill/挂起/恢复))。

您可以了解有关工作使用jobs命令打开交互式 shell(尽管不是其中的所有进程)。 jobs -p将显示进程组 ID。

-x您可以通过向何处发送信号来杀死进程组的成员x进程组 idPGID)或通过使用作业规范与%job-number.例如:

$ sleep 30 | sleep 40 &
[1] 6950 6951
$ ps -j
  PID  PGID   SID TTY          TIME CMD
 6031  6031  6031 pts/3    00:00:00 zsh
 6950  6950  6031 pts/3    00:00:00 sleep
 6951  6950  6031 pts/3    00:00:00 sleep
 6952  6952  6031 pts/3    00:00:00 ps
$ kill -- -6950
[1]  + terminated  sleep 30 | sleep 40
$ sleep 30 | sleep 40 &
[1] 6955 6957
$ jobs
[1]  + running    sleep 30 | sleep 40
$ kill %1
[1]  + terminated  sleep 30 | sleep 40

现在,如果不是从交互式 shell 启动,您的脚本将最终位于与其父进程相同的进程组中。因此,杀死该进程组可能最终会杀死一堆您不想杀死的其他进程。

您可以在脚本中做的就是自己启动进程组。

就像添加:

[ "$(ps -o pgid= -p "$$")" -eq "$$" ] ||
  exec perl -e 'setpgrp or die "setpgrp; $!"; exec @ARGV' -- "$0" "$@"

(如果我们检测到我们不是进程组领导者,则在调用启动新进程组后重新执行脚本perl)在脚本开始处。

这样,您就可以保证脚本将在其自己的进程组中运行。

但这意味着如果你这样做:

something | myscript | something

在交互式 shell 中,很可能myscript不会是进程组领导者。通过执行setpgrp上述操作,脚本将不再位于终端的前台进程组中,这意味着Ctrl-C不会杀死它。

相关内容