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 ...