我有一個bash腳本,用於啓動一個子進程,它不時崩潰(實際上是掛起),沒有明顯的原因(關閉源代碼,所以我沒有太多的辦法可以做到)。因此,我希望能夠在給定的時間內啓動此過程,如果它在給定的時間之後沒有成功返回,就會殺死它。如何在Bash中的給定超時後殺死子進程?
有沒有簡單和健壯方式來實現,使用bash?
P.S .:告訴我這個問題是否更適合serverfault或超級用戶。
我有一個bash腳本,用於啓動一個子進程,它不時崩潰(實際上是掛起),沒有明顯的原因(關閉源代碼,所以我沒有太多的辦法可以做到)。因此,我希望能夠在給定的時間內啓動此過程,如果它在給定的時間之後沒有成功返回,就會殺死它。如何在Bash中的給定超時後殺死子進程?
有沒有簡單和健壯方式來實現,使用bash?
P.S .:告訴我這個問題是否更適合serverfault或超級用戶。
(曾經出現在: BASH FAQ entry #68: "How do I run a command, and have it abort (timeout) after N seconds?")
如果你不介意下載東西,用timeout
( sudo apt-get install timeout
),並使用它像:
timeout 10 ping www.goooooogle.com
如果你不想下載的東西,做什麼超時內部做:
(cmdpid=$BASHPID; (sleep 10; kill $cmdpid) & exec ping www.goooooogle.com)
如果您想要更長的bash的代碼做了超時,使用第二個選項是這樣的:
(cmdpid=$BASHPID;
(sleep 10; kill $cmdpid) \
& while ! ping -w 1 www.goooooogle.com
do
echo crap;
done)
在Ignacio的回覆中,如果有人想知道我做了什麼:'cmdpid = $ BASHPID'將不會接收* calling * shell的pid,但由()開始的(第一個)子shell。 '(睡眠)...東西在第一個子shell中調用第二個子shell在後臺等待10秒,並終止第一個子shell,在啓動殺手子shell進程後,繼續執行其工作負載... – jamadagni 2014-06-08 01:12:50
'timeout '是GNU coreutils的一部分,所以應該已經安裝在所有GNU系統中。 – Sameer 2015-02-13 22:34:26
@Sameer:僅限於版本8. – 2015-02-13 22:37:39
假設你有(或可以很容易地)製作一個用於跟蹤孩子pid的pid文件,那麼你可以創建一個腳本來檢查pid文件的modtime並根據需要殺死/重新生成該進程。然後,只需將腳本放在crontab中即可在大約需要的時間運行。
讓我知道你是否需要更多的細節。如果這聽起來並不像它會滿足您的需求,何談upstart?
sleep 999&
t=$!
sleep 10
kill $t
# Spawn a child process:
(dosmth) & pid=$!
# in the background, sleep for 10 secs then kill that process
(sleep 10 && kill -9 $pid) &
或獲得的退出代碼,以及:
# Spawn a child process:
(dosmth) & pid=$!
# in the background, sleep for 10 secs then kill that process
(sleep 10 && kill -9 $pid) & waiter=$!
# wait on our worker process and return the exitcode
exitcode=$(wait $pid && echo $?)
# kill the waiter subshell, if it still runs
kill -9 $waiter 2>/dev/null
# 0 if we killed the waiter, cause that means the process finished before the waiter
finished_gracefully=$?
在嘗試發送進程可以先處理的信號之前,不應該使用'kill -9'。 – 2011-03-02 02:31:02
的確,我正在尋求一種快速修復方法,只是假設他希望這個過程立即死亡,因爲他說它崩潰了 – Dan 2011-03-02 15:27:56
這實際上是一個非常糟糕的解決方案。如果'dosmth'在2秒鐘內終止,另一個進程將採用舊的pid,並且殺死新的pid? – 2017-01-03 10:26:07
一種方法是在子shell中運行程序,並使用read
命令通過命名管道與子shell進行通信。通過這種方式,您可以檢查正在運行的進程的退出狀態,並通過管道回傳。
下面是在3秒後超時yes
命令的示例。它使用pgrep
獲得進程的PID(可能只適用於Linux)。使用管道也存在一些問題,打開管道進行讀取的過程將掛起,直到它也打開寫入,反之亦然。所以爲了防止read
命令掛起,我已經「楔住」打開管道以用背景子shell讀取。 (另一種方式,以防止凍結打開管道讀寫,即read -t 5 <>finished.pipe
- 但是,這也可能不只是在Linux下工作。)
rm -f finished.pipe
mkfifo finished.pipe
{ yes >/dev/null; echo finished >finished.pipe ; } &
SUBSHELL=$!
# Get command PID
while : ; do
PID=$(pgrep -P $SUBSHELL yes)
test "$PID" = "" || break
sleep 1
done
# Open pipe for writing
{ exec 4>finished.pipe ; while : ; do sleep 1000; done } &
read -t 3 FINISHED <finished.pipe
if [ "$FINISHED" = finished ] ; then
echo 'Subprocess finished'
else
echo 'Subprocess timed out'
kill $PID
fi
rm finished.pipe
我也有這個問題,發現兩件事非常有用:
於是我就用這樣的命令行(OSX 10.9)上:
ping www.goooooogle.com & PING_PID=$(pgrep 'ping'); SECONDS=0; while pgrep -q 'ping'; do sleep 0.2; if [ $SECONDS = 10 ]; then kill $PING_PID; fi; done
由於這是一個循環我收錄了「睡0.2」,以保持涼爽的CPU。 ;-)
(BTW:平安是一個壞榜樣,無論如何,你只是會使用內置的「-t」(超時)選項)
下面是它試圖避免後終止進程的企圖它已經退出,這減少了殺死具有相同進程ID的另一個進程的機會(儘管可能完全避免這種錯誤)。
run_with_timeout()
{
t=$1
shift
echo "running \"$*\" with timeout $t"
(
# first, run process in background
(exec sh -c "$*") &
pid=$!
echo $pid
# the timeout shell
(sleep $t ; echo timeout) &
waiter=$!
echo $waiter
# finally, allow process to end naturally
wait $pid
echo $?
) \
| (read pid
read waiter
if test $waiter != timeout ; then
read status
else
status=timeout
fi
# if we timed out, kill the process
if test $status = timeout ; then
kill $pid
exit 99
else
# if the program exited normally, kill the waiting shell
kill $waiter
exit $status
fi
)
}
使用像run_with_timeout 3 sleep 10000
,它運行sleep 10000
但在3秒後結束它。
這就像使用後臺超時過程在延遲後終止子進程的其他答案。我認爲這與Dan的延伸答案(https://stackoverflow.com/a/5161274/1351983)幾乎相同,只是如果超時外殼已經結束,它不會被終止。
這個程序結束後,仍然會有一些運行的「睡眠」過程,但它們應該是無害的。
這可能比我的其他答案一個更好的解決方案,因爲它不使用非便攜式外殼功能並且不使用pgrep
。
這是我在這裏提交的第三個答案。當收到SIGINT
時,這個人處理信號中斷並清理後臺進程。它使用在top answer中使用的$BASHPID
和exec
技巧來獲取進程的PID(在這種情況下,調用sh
時調用$$
)。它使用FIFO與負責查殺和清理的子shell進行通信。 (這是像我second answer管,但有一個命名管道意味着信號處理程序可以寫進去了。)
run_with_timeout()
{
t=$1 ; shift
trap cleanup 2
F=$$.fifo ; rm -f $F ; mkfifo $F
# first, run main process in background
"[email protected]" & pid=$!
# sleeper process to time out
(sh -c "echo \$\$ >$F ; exec sleep $t" ; echo timeout >$F) &
read sleeper <$F
# control shell. read from fifo.
# final input is "finished". after that
# we clean up. we can get a timeout or a
# signal first.
(exec 0<$F
while : ; do
read input
case $input in
finished)
test $sleeper != 0 && kill $sleeper
rm -f $F
exit 0
;;
timeout)
test $pid != 0 && kill $pid
sleeper=0
;;
signal)
test $pid != 0 && kill $pid
;;
esac
done
) &
# wait for process to end
wait $pid
status=$?
echo finished >$F
return $status
}
cleanup()
{
echo signal >$$.fifo
}
我試圖避免競爭條件,盡我所能。但是,我無法刪除的一個錯誤來源是進程何時結束與超時相同的時間。例如,run_with_timeout 2 sleep 2
或run_with_timeout 0 sleep 0
。因爲它正試圖殺死一個已經自行退出的過程
timeout.sh: line 250: kill: (23248) - No such process
:對我來說,後者給出了一個錯誤。
相關:http://stackoverflow.com/q/601543/132382 – pilcrow 2014-11-08 01:05:20