在一組中啟動後台進程,然後將其全部殺死

在一組中啟動後台進程,然後將其全部殺死

有一天,我嘗試寫一個腳本來殺死一個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進程組 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不會殺死它。

相關內容