
有一天,我嘗試寫一個腳本來殺死一個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
進程組 id發送訊號來殺死進程組的成員(PGID)或透過使用作業規範與%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不會殺死它。