使用 bash 的作業控制捕獲 EXIT 並殺死

使用 bash 的作業控制捕獲 EXIT 並殺死

我有一個運行多個程式的 bash 腳本

#!/bin/sh
python program1.py &
python program2.py &
other programs ... &
lastProgram

我運行它作為./myscript.sh

當我按Ctrl+C關閉它時lastProgram,它退出,所有其他程式繼續在背景運行。問題是其他程序需要終止。

哪種方法可以正確處理從腳本啟動的所有程式的關閉?

答案1

收集進程 ID,退出時終止背景進程。

#!/bin/bash
killbg() {
        for p in "${pids[@]}" ; do
                kill "$p";
        done
}
trap killbg EXIT
pids=()
background job 1 & 
pids+=($!)
background job 2... & 
pids+=($!)
foreground job

EXIT無論原因為何,當 shell 退出時,擷取都會執行該函數。您可以將其更改為trap killbg SIGINT僅在^C.

這不會檢查後台進程之一在腳本嘗試啟動它們之前是否已退出。如果您這樣做,您可能會出錯,或者更糟的是,拍攝錯誤的流程。


或透過工作 ID 殺死他們。讓我們閱讀 的輸出,jobs找出哪些仍然處於活動狀態。

#!/bin/bash 
killjobs() {
    for x in $(jobs | awk -F '[][]' '{print $2}' ) ; do 
        kill %$x
    done
}
trap killjobs EXIT
sleep 999 &
sleep 1 &
sleep 999 &
sleep 30

如果您執行產生其他進程的背景進程(例如子 shell: (sleep 1234 ; echo foo) &),則需要使用set -m(「監視模式」)啟用作業控制才能使其正常運作。否則僅終止引導進程。

答案2

我剛剛讀到類似的問題,關於收集 PID,然後在腳本末尾殺死它們 -問題是一個已完成進程的 PID可以在新流程中回收並重新使用在你的腳本完成之前,然後你可以殺死一個新的(隨機)進程

使用 bash 的作業控制捕獲 EXIT 並殺死

您可以使用 bash 的作業控制僅終止在腳本中使用陷阱和 %n jobspec 啟動的進程,計算可以執行的作業的最大數量(本例中只有 3 個):

#!/bin/bash
#trap 'kill %1 %2 %3' 0     # 0 or EXIT are equivalent
#trap 'kill %1 %2 %3' EXIT  # or use {1..n} as below
trap 'kill %{1..3}' EXIT
sleep 33 &
sleep 33 &
sleep 33 &
echo processes are running, ctrl-c the next sleep
sleep 66
echo this line will never be executed

對已經完成的不存在的作業規範的任何額外「殺死」只會導致錯誤訊息,它不會殺死任何其他新/隨機進程。


殺死腳本的完整進程組

這是一種稍微不同的方式來終止腳本的完整進程組。但是,如果您的腳本/shell 的作業控制未設置,那麼它可以從它的父級繼承它的 PPID...但是如果沒有作業控制,上面的方法也不起作用。

不同之處在於 trap 的kill 指令使用 bash 的 PID,因為它成為新行程的 PGID:

trap 'kill -- -$$' EXIT

這個相關的問題或者在這裡,Johannes“釣魚”Ziemke 捕獲 SIGINT 和 SIGTERM 並用於setsid在新的進程組中殺死整個進程組,這樣我們就不會冒自殺的風險」。

答案3

如果您想終止所有背景進程(如果它們在lastProgram執行完成之前未完成),則需要儲存所有 pid:

python program1.py &
p1pid=$!
python program2.py &
p2pid=$!
other programs ... &
p3pid=$!
lastProgram

kill $p1pid $p2pid $p3pid

如果您只想等到所有後台程序執行完畢後再退出腳本,則可以使用該wait命令。

python program1.py &
python program2.py &
other programs ... &
lastProgram
wait

答案4

這是我的一行命令(在bash):

trap "jobs -p | xargs kill ; trap - INT" INT ; cmd1 & cmd2 & cmd3 &
# replace `cmds` with your command

它的作用是捕獲Ctrl+C來殺死所有後台作業並恢復到其預設行為。

然後,它在後台運行所有命令。

當你點擊Ctrl+時C,它會殺死所有陷阱並將陷阱重置為其預設行為。

相關內容