Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bash: wait with timeout

Tags:

linux

bash

shell

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

app1 & pidApp1=$! app2 & pidApp2=$1  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?

like image 272
user1202136 Avatar asked Apr 05 '12 12:04

user1202136


People also ask

What is bash timeout?

timeout is a command-line utility that runs a specified command and terminates it if it is still running after a given period of time.

Is there a wait command in bash?

The bash wait command is a Shell command that waits for background running processes to complete and returns the exit status. Unlike the sleep command, which waits for a specified time, the wait command waits for all or specific background tasks to finish.

How do I make bash script wait?

How to Use the Bash Sleep Command. Sleep is a very versatile command with a very simple syntax. It is as easy as typing sleep N . This will pause your script for N seconds, with N being either a positive integer or a floating point number.

What is $$ bash?

$$ is a Bash internal variable that contains the Process ID (PID) of the shell running your script. Sometimes the $$ variable gets confused with the variable $BASHPID that contains the PID of the current Bash shell.


2 Answers

Both your example and the accepted answer are overly complicated, why do you not only use timeout since that is exactly its use case? The timeout command even has an inbuilt option (-k) to send SIGKILL after sending the initial signal to terminate the command (SIGTERM by default) if the command is still running after sending the initial signal (see man timeout).

If the script doesn't necessarily require to wait and resume control flow after waiting it's simply a matter of

timeout -k 60s 60s app1 & timeout -k 60s 60s app2 & # [...] 

If it does, however, that's just as easy by saving the timeout PIDs instead:

pids=() timeout -k 60s 60s app1 & pids+=($!) timeout -k 60s 60s app2 & pids+=($!) wait "${pids[@]}" # [...] 

E.g.

$ cat t.sh #!/bin/bash  echo "$(date +%H:%M:%S): start" pids=() timeout 10 bash -c 'sleep 5; echo "$(date +%H:%M:%S): job 1 terminated successfully"' & pids+=($!) timeout 2 bash -c 'sleep 5; echo "$(date +%H:%M:%S): job 2 terminated successfully"' & pids+=($!) wait "${pids[@]}" echo "$(date +%H:%M:%S): done waiting. both jobs terminated on their own or via timeout; resuming script" 

.

$ ./t.sh 08:59:42: start 08:59:47: job 1 terminated successfully 08:59:47: done waiting. both jobs terminated on their own or via timeout; resuming script 
like image 89
Adrian Frühwirth Avatar answered Sep 22 '22 20:09

Adrian Frühwirth


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

like image 41
Aaron Digulla Avatar answered Sep 22 '22 20:09

Aaron Digulla