bash 中未终止进程的超时

bash 中未终止进程的超时

我有一个正在运行的主脚本,我想从它开始第二个“缓慢的过程”,如果主脚本没有在时间限制内完成,则在主脚本中“做某事”——具体取决于是否完成。注意:如果“缓慢的过程”在我的时间限制之前完成,我不想等待整个时间限制。

我希望“缓慢的过程”继续进行,这样我就可以收集有关其性能的统计数据和取证。

我研究过使用暂停,但是完成后它会杀死我的脚本。

假设这个简化的例子。

主目录

result=`timeout 3 ./slowprocess.sh`
if [ "$result" = "Complete" ]
then
 echo "Cool it completed, do stuff..."
else
 echo "It didn't complete, do something else..."
fi

慢进程.sh

#!/bin/bash
start=`date +%s`
sleep 5
end=`date +%s`
total=`expr $end - $start`
echo $total >> /tmp/performance.log
echo "Complete"

在这里,它使用超时 - 所以脚本会死掉,所以什么也不会发生/tmp/performance.log- 我想slowprocess.sh完成,但是,我想main.sh继续下一步,即使它没有在 3 秒内完成。

答案1

使用 ksh/bash/zsh:

{
  (./slowprocess.sh >&3 3>&-; echo "$?") |
    if read -t 3 status; then
      echo "Cool it completed with status $status, do stuff..."
    else
      echo "It didn't complete, do something else..."
    fi
} 3>&1

我们将原始的 stdout 复制到 fd 3 ( ) 上,以便我们可以为( )3>&1恢复它,而子 shell 其余部分的 stdout 则转到管道 to 。slowprocess.sh>&3(...)read -t 3

或者,如果您想使用timeout(这里假设 GNU timeout):

timeout --foreground 3 sh -c './slowprocess.sh;exit'

将避免slowprocess.sh被杀死(这对于通过执行 shell 进程中的最后一个命令来优化的实现;exit是必要的)。sh

答案2

这是仅使用普遍存在的 shell 工具的解决方案。

sleep这应该可以通过在后台分叉缓慢的进程和 a 并等待第一个进程完成来轻松完成,除了waitshell 内置等待全部工作要完成,而不仅仅是第一个工作。

因此,请sleep在后台分叉缓慢的进程和 a,让它们都通过管道报告其状态,并读取从管道中出来的第一个状态。

fifo=$(mktemp -u)  # if not on Linux, adapt to what your OS provides
mkfifo -m 600 "$fifo"
{ ./slowprocess.sh; echo z >"$fifo"; } &
sh -c 'sleep 3; echo a' >"$fifo" &
sleep_pgid=$!
read status <$fifo
case $status in
  a) echo "That process is taking a long time"; read ignored <$fifo;;
  z) echo "Done already"; kill -INT -$sleep_pgid;;
esac
rm "$fifo"

答案3

无论发生什么阻塞,slowprocess.sh都可以在后台进行,以便即使在超时后也可以继续执行。

您也可以使用signalfrom timeoutback 到您的main.sh.请参阅 的手册页timeout

摘自timeout信息页

`-s SIGNAL'
`--signal=SIGNAL'
     Send this SIGNAL to COMMAND on timeout, rather than the default
     `TERM' signal. SIGNAL may be a name like `HUP' or a number. Also
     see *Note Signal specifications::.

您必须在 内捕获此信号main.sh

timeout针对不同类型的故障返回不同的状态。您或许也可以利用这些:

   Exit status:

     124 if COMMAND times out
     125 if `timeout' itself fails
     126 if COMMAND is found but cannot be invoked
     127 if COMMAND cannot be found
     the exit status of COMMAND otherwise

$?行运行后,它们出现在变量 中timeout ...

答案4

这是另一种相当简单的方法,尽管它会产生一些无关的作业控制输出:启动后台进程“sleep nnn”,然后使用“wait -n”等待第一个睡眠进程和您正在监视的进程。这是该技术的一个有点笨拙的示例(使用“set -o notification”即时通知):

$ (for I in $(seq 10); do sleep 1; echo $I ; done) & \
more> J1=$! ; \
more> sleep 5 & \
more> J2=$! ; \
more> time wait -n $J1 $J2
[1] 288160
[2] 288161
1
2
3
4
[2]+  Done                    sleep 5

real    0m5.006s
user    0m0.002s
sys 0m0.004s
$ 5
6
7
8
9
10
[1]+  Done                    ( for I in $(seq 10);
do
sleep 1; echo $I;
done )
$

$ (for I in $(seq 3); do sleep 1; echo $I ; done) & \
more> J1=$! ; \
more> sleep 5 & \
more> J2=$! ; \
more> time wait -n $J1 $J2
[1] 288256
[2] 288257
1
2
3
[1]-  Done                    ( for I in $(seq 3);
do
sleep 1; echo $I;
done )

real    0m3.025s
user    0m0.004s
sys 0m0.015s
$ [2]+  Done                    sleep 5
$

相关内容