
我有一個運行多個程式的 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,它會殺死所有陷阱並將陷阱重置為其預設行為。