击:用等待超时(Bash: wait with timeout)

2019-06-23 10:07发布

在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只能在同一壳体内推出了PID被调用。

有任何想法吗?

Answer 1:

写的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的所有儿童的脚本。 看到这个问题的答案了不同的方式来实现这个功能: 最好的方式杀死所有的子进程

如果你想一步BASH之外,你可以写的PID和超时到一个目录,看该目录。 每分钟左右,读条目,并检查其过程仍然存在,以及它们是否已超时。

编辑如果你想知道该进程是否已经成功地死了,你可以使用kill -0 $pid

EDIT2或者你可以尝试进程组。 kevinarpe说:要获得PGID的PID(146322):

ps -fjww -p 146322 | tail -n 1 | awk '{ print $4 }'

在我的情况:145974.然后PGID可以杀一个特殊的选项可用于终止所有过程组中: kill -- -145974



Answer 2:

无论你的榜样和接受的答案过于复杂,为什么你不仅可以使用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[@]}"
# [...]

$ 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


Answer 3:

这里是亚伦Digulla的答案,它使用的简化版本kill -0把戏,亚伦Digulla叶评论:

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,几乎可以肯定。



Answer 4:

我写了一个bash的功能,将等到完成的PID或直到超时,返回非零如果超时超标和打印所有的PID不finisheds。

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



Answer 5:

把我的2C,我们可以boild下来特谢拉的解决方案:

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毫秒= 1千赫=充裕的时间。 然而,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没有超时参数? 也许因为timeoutkill -0waitwait -n命令可以更准确地我们想要的告诉机器。

为什么wait摆在首位内置打坏,这样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失败,你很难知道为什么。 除非你写的过程中,或它的手册名的情况下,没有办法确定一个合理的超时值。

  • 当你写的过程中,你可以实现一个适当的期限处理,甚至回应“奥夫Wiedersehen!” 通过命名管道发送给它。 然后,你有一些地面甚至像咒语try_wait :-)



文章来源: Bash: wait with timeout
标签: linux bash shell