Bash:等待超时
在一个Bash脚本中,我想要做一些事情:
app1 & pidApp1=$! app2 & pidApp2=$1 timeout 60 wait $pidApp1 $pidApp2 kill -9 $pidApp1 $pidApp2
也就是说,在后台启动两个应用程序,并给他们60秒完成他们的工作。 那么,如果他们没有在这段时间内完成,杀死他们。
不幸的是,上述不起作用,因为timeout
是一个可执行文件,而wait
是一个shell命令。 我试图改变它:
timeout 60 bash -c wait $pidApp1 $pidApp2
但是这仍然不起作用,因为只能在同一个shell中启动的PID上调用wait
。
有任何想法吗?
将PID写入文件并像下面这样启动应用程序:
pidFile=... ( app ; rm $pidFile ; ) & pid=$! echo $pid > $pidFile ( sleep 60 ; if [[ -e $pidFile ]]; then killChildrenOf $pid ; fi ; ) & killerPid=$! wait $pid kill $killerPid
这将创build另一个进程,睡眠暂停和杀死过程,如果它还没有完成到目前为止。
如果进程更快完成,PID文件被删除,杀手进程终止。
killChildrenOf
是一个脚本,用于获取所有进程并杀死某个PID的所有subprocess。 查看此问题的答案,以实现此function的不同方式: 杀死所有subprocess的最佳方法
如果你想跨出BASH之外,你可以把PID和超时写入一个目录并观察那个目录。 每隔一分钟左右,阅读条目并检查哪些进程仍在,以及是否超时。
编辑如果你想知道进程是否成功,你可以使用kill -0 $pid
编辑2或者你可以尝试处理组。 kevinarpe说:要得到一个PID的PGID(146322):
ps -fjww -p 146322 | tail -n 1 | awk '{ print $4 }'
在我的情况下:145974.然后PGID可以用kill的特殊选项来终止一个组中的所有进程: kill -- -145974
你的例子和被接受的答案都过于复杂了,为什么你不仅使用timeout
因为这正是它的用例呢? 如果命令在发送初始信号(参见man timeout
)后仍然在运行,则timeout
命令甚至会在发送初始信号以终止命令(缺省为SIGTERM
)后发送SIGKILL
内置选项( -k
)。
如果脚本不一定需要wait
并在等待之后恢复控制stream量,那么这只是一个问题
timeout -k 60s 60s app1 & timeout -k 60s 60s app2 & # [...]
但是,如果是这样,保存timeout
PID就变得简单了:
pids=() timeout -k 60s 60s app1 & pids+=($!) timeout -k 60s 60s app2 & pids+=($!) wait "${pids[@]}" # [...]
例如
$ cat t.sh #!/bin/bash echo "$(date +%H:%M:%S): start" pids=() timeout 10 bash -c 'sleep 5; echo "$(date +%H:%M:%S): job 1 terminated successfully"' & pids+=($!) timeout 2 bash -c 'sleep 5; echo "$(date +%H:%M:%S): job 2 terminated successfully"' & pids+=($!) wait "${pids[@]}" echo "$(date +%H:%M:%S): done waiting. both jobs terminated on their own or via timeout; resuming script"
。
$ ./t.sh 08:59:42: start 08:59:47: job 1 terminated successfully 08:59:47: done waiting. both jobs terminated on their own or via timeout; resuming script
下面是Aaron Digulla的答案的简化版本,它使用Aaron Digulla留下的评论中的kill -0
技巧:
app & pidApp=$! ( sleep 60 ; echo 'timeout'; kill $pidApp ) & killerPid=$! wait $pidApp kill -0 $killerPid && kill $killerPid
在我的情况下,我想既是set -e -x
安全set -e -x
并返回状态码,所以我用:
set -e -x app & pidApp=$! ( sleep 45 ; echo 'timeout'; kill $pidApp ) & killerPid=$! wait $pidApp status=$? (kill -0 $killerPid && kill $killerPid) || true exit $status
退出状态143表示SIGTERM,几乎肯定是从我们的超时。
我写了一个bash函数,它将等到PID结束或超时,如果超时超时返回非零,并打印所有未完成的PID。
function wait_timeout { local limit=${@:1:1} local pids=${@:2} local count=0 while true do local have_to_wait=false for pid in ${pids}; do if kill -0 ${pid} &>/dev/null; then have_to_wait=true else pids=`echo ${pids} | sed -e "s/${pid}//g"` fi done if ${have_to_wait} && (( $count < $limit )); then count=$(( count + 1 )) sleep 1 else echo ${pids} return 1 fi done return 0 }
要使用这只是wait_timeout $timeout $PID1 $PID2 ...