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?
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.
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 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.
$$ 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.
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With