8

Is there any built in feature in bash to wait for 1 out of many processes to finish? And then kill remaining processes?

pids=""
# Run five concurrent processes
for i in {1..5}; do
        ( longprocess ) &
        # store PID of process
        pids+=" $!"
done

if [ "one of them finished" ]; then
        kill_rest_of_them;
fi

I'm looking for "one of them finished" command. Is there any?

2 Answers2

13

bash 4.3 added a -n flag to the built-in wait command, which causes the script to wait for the next child to complete. The -p option to jobs also means you don't need to store the list of pids, as long as there aren't any background jobs that you don't want to wait on.

# Run five concurrent processes
for i in {1..5}; do
    ( longprocess ) &
done

wait -n
kill $(jobs -p)

Note that if there is another background job other than the 5 long processes that completes first, wait -n will exit when it completes. That would also mean you would still want to save the list of process ids to kill, rather than killing whatever jobs -p returns.

GreenGiant
  • 4,930
  • 1
  • 46
  • 76
chepner
  • 497,756
  • 71
  • 530
  • 681
4

It's actually fairly easy:

#!/bin/bash
set -o monitor
killAll()
{
# code to kill all child processes
}

# call function to kill all children on SIGCHLD from the first one
trap killAll SIGCHLD

# start your child processes here

# now wait for them to finish
wait

You just have to be really careful in your script to use only bash built-in commands. You can't start any utilities that run as a separate process after you issue the trap command - any child process exiting will send SIGCHLD - and you can't tell where it came from.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • 1
    Certainly possible to distinguish other SIGCHLDs. Store a list of your background processes (`foo & foo_pids+=( $! )`), and check if any of them are dead when you get a SIGCHLD (via `kill -0`). Everyone's alive -> it came from somewhere else. – Charles Duffy Jun 03 '16 at 18:38
  • @CharlesDuffy Nice. I wish I'd thought of that when I had to hack together a script for a customer similar to what I posted above, all under extreme time pressure. – Andrew Henle Jun 03 '16 at 18:47
  • 2
    A new special parameter (perhaps `$&`) containing the process ID of the child the caused `SIGCHILD` (or that caused `wait -n` to exit) would be nice. Maybe I'll make a feature request for 4.5 – chepner Jun 03 '16 at 18:50