How to kill a child process after a given timeout in Bash?

前端 未结 8 1742
青春惊慌失措
青春惊慌失措 2020-11-22 15:24

I have a bash script that launches a child process that crashes (actually, hangs) from time to time and with no apparent reason (closed source, so there isn\'t much I can do

相关标签:
8条回答
  • 2020-11-22 15:48

    I also had this question and found two more things very useful:

    1. The SECONDS variable in bash.
    2. The command "pgrep".

    So I use something like this on the command line (OSX 10.9):

    ping www.goooooogle.com & PING_PID=$(pgrep 'ping'); SECONDS=0; while pgrep -q 'ping'; do sleep 0.2; if [ $SECONDS = 10 ]; then kill $PING_PID; fi; done
    

    As this is a loop I included a "sleep 0.2" to keep the CPU cool. ;-)

    (BTW: ping is a bad example anyway, you just would use the built-in "-t" (timeout) option.)

    0 讨论(0)
  • 2020-11-22 15:56

    Here's the third answer I've submitted here. This one handles signal interrupts and cleans up background processes when SIGINT is received. It uses the $BASHPID and exec trick used in the top answer to get the PID of a process (in this case $$ in a sh invocation). It uses a FIFO to communicate with a subshell that is responsible for killing and cleanup. (This is like the pipe in my second answer, but having a named pipe means that the signal handler can write into it too.)

    run_with_timeout ()
    {
      t=$1 ; shift
    
      trap cleanup 2
    
      F=$$.fifo ; rm -f $F ; mkfifo $F
    
      # first, run main process in background
      "$@" & pid=$!
    
      # sleeper process to time out
      ( sh -c "echo \$\$ >$F ; exec sleep $t" ; echo timeout >$F ) &
      read sleeper <$F
    
      # control shell. read from fifo.
      # final input is "finished".  after that
      # we clean up.  we can get a timeout or a
      # signal first.
      ( exec 0<$F
        while : ; do
          read input
          case $input in
            finished)
              test $sleeper != 0 && kill $sleeper
              rm -f $F
              exit 0
              ;;
            timeout)
              test $pid != 0 && kill $pid
              sleeper=0
              ;;
            signal)
              test $pid != 0 && kill $pid
              ;;
          esac
        done
      ) &
    
      # wait for process to end
      wait $pid
      status=$?
      echo finished >$F
      return $status
    }
    
    cleanup ()
    {
      echo signal >$$.fifo
    }
    

    I've tried to avoid race conditions as far as I can. However, one source of error I couldn't remove is when the process ends near the same time as the timeout. For example, run_with_timeout 2 sleep 2 or run_with_timeout 0 sleep 0. For me, the latter gives an error:

    timeout.sh: line 250: kill: (23248) - No such process
    

    as it is trying to kill a process that has already exited by itself.

    0 讨论(0)
提交回复
热议问题