Bash – Kill a process if it goes quiet for a certain amount of time

bashlinuxshellstdouttimeout

I have a utility that has a nasty habit of going quiet and staying there, I already know how long into the process it does this so I am using timeout to fight this, but sometimes it does it before that time. Is there a tool similar to timeout that will kill the process if it stops directing output to stdout?

Best Answer

With zsh, you could do:

zmodload zsh/system
coproc your-command
while :; do
  sysread -t 10 -o 1 <&p && continue
  if (( $? == 4 )); then
    echo "Timeout" >&2
    kill $!
  fi
  break
done

The idea being to use the -t option of sysread to read from your-command output with a timeout.

Note that it makes your-command's output a pipe. It may be that your-command starts buffering its output when it doesn't go to a terminal, in which case you may find that it doesn't output anything in a while, but only because of that buffering, not because it's hung somehow.

You could work around that by using stdbuf -oL your-command to restore line-buffering (if your-command uses stdio) or use zpty instead of coproc to fake a terminal output.

With bash, you'd have to rely on dd and GNU timeout if available:

coproc your-command
while :; do
  timeout 10 dd bs=8192 count=1 2> /dev/null <&${COPROC[0]} && continue
  if (($? == 124)); then
    echo Timeout >&2
    kill "$!"
  fi
done

Instead of coproc, you could also use process substitution:

while :; do
  timeout 10 dd bs=8192 count=1 2> /dev/null <&3 && continue
  if (($? == 124)); then
    echo Timeout >&2
    kill "$!"
  fi
done 3< <(your-command)

(that won't work in zsh or ksh93 because $! doesn't contain the pid of your-command there).