在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
但是这仍然不起作用,因为wait
只能在同一个shell中启动的PID上调用。
有什么想法吗?
答案 0 :(得分:54)
您的示例和接受的答案都过于复杂,为什么仅使用timeout
,因为完全其用例?如果命令仍在运行,timeout
命令甚至有一个内置选项(-k
)发送SIGKILL
后发送初始信号以终止命令(默认为SIGTERM
)发送初始信号后(见man timeout
)。
如果脚本不一定需要wait
并在等待之后恢复控制流程,那只是
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[@]}"
# [...]
E.g。
$ 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
答案 1 :(得分:21)
将PID写入文件并启动这样的应用程序:
pidFile=...
( app ; rm $pidFile ; ) &
pid=$!
echo $pid > $pidFile
( sleep 60 ; if [[ -e $pidFile ]]; then killChildrenOf $pid ; fi ; ) &
killerPid=$!
wait $pid
kill $killerPid
这将创建另一个进程,该进程会在超时时间内休眠,如果到目前为止尚未完成,则会终止进程。
如果进程加快,则删除PID文件并终止杀手进程。
killChildrenOf
是一个脚本,它获取所有进程并杀死某个PID的所有子进程。有关实现此功能的不同方法,请参阅此问题的答案:Best way to kill all child processes
如果您想要走出BASH,可以将PID和超时写入目录并观察该目录。每分钟左右,阅读条目并检查哪些进程仍然存在,以及它们是否已超时。
编辑如果您想知道该过程是否已成功死亡,您可以使用kill -0 $pid
EDIT2 或者您可以尝试进程组。 kevinarpe说:获取PID的PIDID(146322):
ps -fjww -p 146322 | tail -n 1 | awk '{ print $4 }'
在我的情况下:145974.然后PGID可以与kill的特殊选项一起使用来终止组中的所有进程:kill -- -145974
答案 2 :(得分:3)
这是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
app &
pidApp=$!
( sleep 45 ; echo 'timeout'; kill $pidApp ) &
killerPid=$!
wait $pidApp
status=$?
(kill -0 $killerPid && kill $killerPid) || true
exit $status
退出状态143表示SIGTERM,几乎可以肯定是我们的超时。
答案 3 :(得分:1)
我写了一个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 ...
答案 4 :(得分:0)
要放入我的2c,我们可以将Teixeira的解决方案归结为:
try_wait() {
# Usage: [PID]...
for ((i = 0; i < $#; i += 1)); do
kill -0 $@ && sleep 0.001 || return 0
done
return 1 # timeout or no PIDs
} &>/dev/null
Bash的sleep
接受小数秒,并且0.001s = 1 ms = 1 KHz =充足的时间。但是,UNIX在文件和进程方面没有漏洞。 try_wait
完成的很少。
$ cat &
[1] 16574
$ try_wait %1 && echo 'exited' || echo 'timeout'
timeout
$ kill %1
$ try_wait %1 && echo 'exited' || echo 'timeout'
exited
我们必须回答一些棘手的问题才能进一步了解。
为什么wait
没有超时参数?也许是因为timeout
,kill -0
,wait
和wait -n
命令可以更准确地告诉机器我们想要什么。
为什么wait
首先内置在Bash中,所以timeout wait PID
无法正常工作?也许只有这样,Bash才能实施适当的信号处理。
考虑:
$ timeout 30s cat &
[1] 6680
$ jobs
[1]+ Running timeout 30s cat &
$ kill -0 %1 && echo 'running'
running
$ # now meditate a bit and then...
$ kill -0 %1 && echo 'running' || echo 'vanished'
bash: kill: (NNN) - No such process
vanished
无论是在材料领域还是在机器领域,我们都需要一些 运行的基础,我们也需要等待的基础。
当kill
失败时,您几乎不知道为什么。除非你写
该过程或其手册中提到的情况,没有办法
确定合理的超时值。
编写该过程后,您可以实现适当的TERM处理程序,甚至响应“ Auf Wiedersehen!”。通过命名管道发送给它。然后,即使对于try_wait
之类的咒语,您也有一定的基础:-)
答案 5 :(得分:0)
app1 &
app2 &
sleep 60 &
wait -n