前几天我尝试编写一个脚本来杀死一个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
Run Code Online (Sandbox Code Playgroud)
我知道不需要此函数download,但我将其包含在此处是因为它是我在此脚本中多次使用的函数。另外,我给的最大时限300来curl,但是,在某些网络错误,它也被卡住。
通常,如果您从交互式 shell 调用您的脚本,它将被放入一个新的进程组(又名job)中,因此如果您Ctrl-C使用它,该脚本启动的所有进程都将收到一个 SIGINT。
如果您也在后台启动它(仍然从交互式 shell 启动),它也将在其自己的进程组中启动。
您可以通过以下方式了解进程组:
ps -j
Run Code Online (Sandbox Code Playgroud)
(j为job control这是炮弹,在进程组运行命令行能够管理他们的这种行为(前景/背景/杀/暂停/恢复))。
您可以使用该命令了解交互式 shell的作业jobs(尽管不是其中的所有进程)。 jobs -p将向您显示进程组 ID。
您可以通过向进程组 ID ( PGID )-x所在位置发送信号或使用带有. 例如:x%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
Run Code Online (Sandbox Code Playgroud)
现在,如果不是从交互式 shell 启动,您的脚本将在与其父进程相同的进程组中结束。因此,杀死该进程组最终可能会杀死一堆您不想杀死的其他进程。
您可以在脚本中做的是自己启动进程组。
喜欢通过添加:
[ "$(ps -o pgid= -p "$$")" -eq "$$" ] ||
exec perl -e 'setpgrp or die "setpgrp; $!"; exec @ARGV' -- "$0" "$@"
Run Code Online (Sandbox Code Playgroud)
(perl如果我们检测到我们不是进程组组长,则在调用启动新进程组后重新执行脚本)在脚本的开头。
这样,您就可以保证脚本将在其自己的进程组中运行。
这意味着如果你这样做:
something | myscript | something
Run Code Online (Sandbox Code Playgroud)
在交互式 shell 中,很myscript可能不是进程组组长。执行setpgrp上述操作,脚本将不再位于终端的前台进程组中,这意味着Ctrl-C不会杀死它。