linuxbashshelltimeoutwaitpid

Bash: wait with timeout


In a Bash script, I would like to do something like:

app1 &
pidApp1=$!
app2 &
pidApp2=$!

timeout 60 wait $pidApp1 $pidApp2
kill -9 $pidApp1 $pidApp2

I.e., launch two applications in the background, and give them 60 seconds to complete their work. Then, if they don't finish within that interval, kill them.

Unfortunately, the above does not work, since timeout is an executable, while wait is a shell command. I tried changing it to:

timeout 60 bash -c wait $pidApp1 $pidApp2

But this still does not work, since wait can only be called on a PID launched within the same shell.

Any ideas?


Solution

  • Write the PIDs to files and start the apps like this:

    pidFile=...
    ( app ; rm $pidFile ; ) &
    pid=$!
    echo $pid > $pidFile
    ( sleep 60 ; if [[ -e $pidFile ]]; then killChildrenOf $pid ; fi ; ) &
    killerPid=$!
    
    wait $pid
    kill $killerPid
    

    That would create another process that sleeps for the timeout and kills the process if it hasn't completed so far.

    If the process completes faster, the PID file is deleted and the killer process is terminated.

    killChildrenOf is a script that fetches all processes and kills all children of a certain PID. See the answers of this question for different ways to implement this functionality: Best way to kill all child processes

    If you want to step outside of BASH, you could write PIDs and timeouts into a directory and watch that directory. Every minute or so, read the entries and check which processes are still around and whether they have timed out.

    EDIT If you want to know whether the process has died successfully, you can use kill -0 $pid

    EDIT2 Or you can try process groups. kevinarpe said: To get PGID for a PID(146322):

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

    In my case: 145974. Then PGID can be used with a special option of kill to terminate all processes in a group: kill -- -145974