有一天,我尝试编写一个脚本来杀死一个PID及其所有子进程,但在花了一些时间之后,我认为它不值得信赖,因为有时一些子进程最终会得到一个PPID的1。
现在,我正在寻找的是如何在后台运行以下函数,并且使该函数和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
,但我将其包含在此处,因为这是我在此脚本中多次使用的函数。另外,我给出的最大时间限制为300到curl
,但根据一些网络错误,它也会卡住。
答案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不会杀死它。