带超时的直通管道

带超时的直通管道

当没有数据通过管道传递时,如何实现超时?它将充当看门狗,用法如下:

process1 | watchdog --timeout 60 | process2

当没有数据管道通过时,我希望关闭看门狗进程。

谢谢!

答案1

首先,您问题中的看门狗管道可能不会终止,process1除非它再次尝试写入死管道。所以你的看门狗应该以某种方式显式地杀死 process1。

除此之外还有一个非常简单的watchdog.shshell 脚本。您可以在控制台中以交互方式测试它。只需输入./watchdog.sh.它将复制您输入的所有内容,并在您 5 秒内未输入任何内容时停止。

#!/bin/bash

# first arg is timeout (default: 5)
T="${1:-5}"
PID=$$

exec tee >(bash -c '
while true ; do
    bytes=$(timeout '$T' cat | wc -c)
    if ! [ "$bytes" -gt 0 ] ;then
            break
    fi
done
## add something like "killall process1", for now we just kill this tee command
kill -9 '$PID)

请注意,脚本实际上会在 T 和 2*T 之间超时(否则会更复杂)。正如我最初提到的那样,您可以以某种方式添加一种杀死方式process1

下面举一个例子进行测试。

进程1.sh:

#!/bin/bash

echo "here we are ..."
sleep 2
echo "still alive ..."
sleep 20
echo "too late ..."

并像这样运行它(包括一个在超时时终止 process1.sh 的丑陋方法):

(./process1.sh & echo $! >/tmp/pid; wait) |(./watchdog.sh 5; kill `cat /tmp/pid`)

答案2

这在性能方面可能并不理想,并且它是基于行的,但read命令的超时功能可能用于此目的。在 中bash,您可以使用如下所示的命令行节作为您的watchdog --timeout 60部分:

while read -r -s -t 60 line ; do printf "%s\n" "$line" ; done

zsh提供了类似的read命令。

但请注意,它实际上不会终止整个命令,直到第一部分(即您的process1)由于其标准输出被关闭而决定终止。

答案3

该问题的另一种观点可能是将看门狗从管道中删除,并统计管道的上次修改时间。例如,

(
process1 &
pid=$!
old=0
while   new=$(stat -L /dev/stderr -c %Y)
        [ $new != $old ]
do      old=$new
        sleep 60
done 3>&2 2>&1 1>&3   &&   kill -hup $pid
) |
process2

这会将 process1 置于后台,以便我们可以获得其 pid,然后轮询输出管道的最后修改时间。如果60秒内没有变化,我们就可以杀死这个pid。为了能够捕获输出,stat我们将文件描述符 1(即管道)交换为 stderr 和 stat。

相关内容