Linux Bash:等待超时
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10028820/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bash: wait with timeout
提问by user1202136
In a Bash script, I would like to do something like:
在 Bash 脚本中,我想做如下事情:
app1 &
pidApp1=$!
app2 &
pidApp2=
timeout 60 wait $pidApp1 $pidApp2
kill -9 $pidApp1 $pidApp2
I.e., launch two applications in the background, and give them 60 seconds to complete their work. Then, if they don't finish within that interval, kill them.
即,在后台启动两个应用程序,并给它们 60 秒的时间来完成它们的工作。然后,如果他们没有在那个时间间隔内完成,就杀死他们。
Unfortunately, the above does not work, since timeout
is an executable, while wait
is a shell command. I tried changing it to:
不幸的是,上面的方法不起作用,因为它timeout
是一个可执行文件,而它wait
是一个 shell 命令。我尝试将其更改为:
timeout 60 bash -c wait $pidApp1 $pidApp2
But this still does not work, since wait
can only be called on a PID launched within the same shell.
但这仍然不起作用,因为wait
只能在同一个 shell 中启动的 PID 上调用。
Any ideas?
有任何想法吗?
采纳答案by Aaron Digulla
Write the PIDs to files and start the apps like this:
将 PID 写入文件并像这样启动应用程序:
pidFile=...
( app ; rm $pidFile ; ) &
pid=$!
echo $pid > $pidFile
( sleep 60 ; if [[ -e $pidFile ]]; then killChildrenOf $pid ; fi ; ) &
killerPid=$!
wait $pid
kill $killerPid
That would create another process that sleeps for the timeout and kills the process if it hasn't completed so far.
这将创建另一个进程,该进程在超时时休眠并在该进程尚未完成时终止该进程。
If the process completes faster, the PID file is deleted and the killer process is terminated.
如果进程完成得更快,则删除 PID 文件并终止杀手进程。
killChildrenOf
is a script that fetches all processes and kills all children of a certain PID. See the answers of this question for different ways to implement this functionality: Best way to kill all child processes
killChildrenOf
是一个脚本,它获取所有进程并杀死某个 PID 的所有子进程。有关实现此功能的不同方法,请参阅此问题的答案:Best way to kill all child processes
If you want to step outside of BASH, you could write PIDs and timeouts into a directory and watch that directory. Every minute or so, read the entries and check which processes are still around and whether they have timed out.
如果你想跳出 BASH,你可以将 PID 和超时写入一个目录并观察该目录。每隔一分钟左右,阅读条目并检查哪些进程仍然存在以及它们是否已超时。
EDITIf you want to know whether the process has died successfully, you can use kill -0 $pid
EDIT如果你想知道进程是否已经成功死亡,你可以使用kill -0 $pid
EDIT2Or you can try process groups. kevinarpesaid: To get PGID for a PID(146322):
EDIT2或者您可以尝试进程组。kevinarpe说:要获取 PID(146322) 的 PGID:
ps -fjww -p 146322 | tail -n 1 | awk '{ print }'
In my case: 145974. Then PGID can be used with a special option of kill to terminate all processes in a group: kill -- -145974
在我的例子中:145974。然后 PGID 可以与 kill 的特殊选项一起使用来终止组中的所有进程: kill -- -145974
回答by Bryan Larsen
Here's a simplified version of Aaron Digulla's answer, which uses the kill -0
trick that Aaron Digulla leaves in a comment:
这是 Aaron Digulla 答案的简化版本,它使用了kill -0
Aaron Digulla 在评论中留下的技巧:
app &
pidApp=$!
( sleep 60 ; echo 'timeout'; kill $pidApp ) &
killerPid=$!
wait $pidApp
kill -0 $killerPid && kill $killerPid
In my case, I wanted to be both set -e -x
safe and return the status code, so I used:
就我而言,我希望既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
An exit status of 143 indicates SIGTERM, almost certainly from our timeout.
退出状态 143 表示 SIGTERM,几乎可以肯定来自我们的超时。
回答by Adrian Frühwirth
Both your example and the accepted answer are overly complicated, why do you not onlyuse timeout
since that is exactlyits use case? The timeout
command even has an inbuilt option (-k
) to send SIGKILL
after sending the initial signal to terminate the command (SIGTERM
by default) if the command is still running after sending the initial signal (see man timeout
).
您的示例和接受的答案都过于复杂,为什么您不仅使用它,timeout
因为这正是它的用例?如果命令在发送初始信号后仍在运行(参见),则该timeout
命令甚至有一个内置选项 ( -k
)SIGKILL
在发送初始信号后发送以终止命令(SIGTERM
默认情况下man timeout
)。
If the script doesn't necessarily require to wait
and resume control flow after waiting it's simply a matter of
如果脚本不一定需要wait
并在等待后恢复控制流,那么这只是一个问题
timeout -k 60s 60s app1 &
timeout -k 60s 60s app2 &
# [...]
If it does, however, that's just as easy by saving the timeout
PIDs instead:
但是,如果确实如此,那么通过保存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
回答by JonatasTeixeira
I wrote a bash function that will wait until PIDs finished or until timeout, that return non zero if timeout exceeded and print all the PIDs not finisheds.
我编写了一个 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
}
To use this is just wait_timeout $timeout $PID1 $PID2 ...
使用这个只是 wait_timeout $timeout $PID1 $PID2 ...
回答by Andreas Spindler
To put in my 2c, we can boild down Teixeira's solution to:
为了放入我的 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's sleep
accepts fractional seconds, and 0.001s = 1 ms = 1 KHz = plenty of time. However, UNIX has no loopholes when it comes to files and processes. try_wait
accomplishes very little.
Bashsleep
接受小数秒,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
We have to answer some hard questions to get further.
我们必须回答一些棘手的问题才能走得更远。
Why has wait
no timeout parameter? Maybe because the timeout
, kill -0
, wait
and wait -n
commands can tell the machine more precisely what we want.
为什么wait
没有超时参数?也许是因为timeout
, kill -0
,wait
和wait -n
命令可以更准确地告诉机器我们想要什么。
Why is wait
builtin to Bash in the first place, so that timeout wait PID
is not working? Maybe only so Bash can implement proper signal handling.
为什么首先wait
内置到 Bash 中,所以它timeout wait PID
不起作用?也许只有这样 Bash 才能实现正确的信号处理。
Consider:
考虑:
$ 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
Whether in the material world or in machines, as we require some ground on which to run, we require some ground on which to wait too.
无论是在物质世界还是在机器中,因为我们需要一些可以运行的地面,我们也需要一些可以等待的地面。
When
kill
fails you hardly know why. Unless you wrote the process, or its manual names the circumstances, there is no way to determine a reasonable timeout value.When you have written the process, you can implement a proper TERM handler or even respond to "Auf Wiedersehen!" send to it through a named pipe. Then you have some ground even for a spell like
try_wait
:-)
当
kill
失败时,你几乎不知道为什么。除非你编写了进程,或者它的手册命名了情况,否则没有办法确定一个合理的超时值。编写流程后,您可以实施适当的 TERM 处理程序,甚至可以响应“Auf Wiedersehen!” 通过命名管道发送给它。然后,即使是像
try_wait
:-)这样的咒语,你也有一些基础
回答by user1931823
app1 &
app2 &
sleep 60 &
wait -n