marmistrz
marmistrz

Reputation: 6392

Exit a bash script when one of the subprocesses exits

I'm running several background processes in my script

run_gui()
{
    exec ... # the real commands here
}

The functions run_ai1(), run_ai2 are analogous.

Then I run the functions and do the needed piping

run_gui &
run_ai1 &
run_ai2 &
while true; do
    while true; do
        read -u $ai1_outfd line || echo "Nothing read"
        if [[ $line ]]; then
            : # processing
        fi
    done
    sleep $turndelay
    while true; do
        read -u $ai2_outfd line || echo "nothing read"
        if [[ $line ]]; then
            : # processing
        fi
    done
    sleep $turndelay
done

If any of those three processes exits, I want to check their exit codes and terminate the rest of the processes. For example, if run_ai2 exits with exit code 3, then I want to stop the processes run_ai1 and run_gui and exit the main script with exit code 1. The correct exitcodes for the different backgrounds processes may differ.

The problem is: how can I detect it? There's the command wait but I don't know in advance which script will finish first. I could run wait as a background process - but it's becoming even more clumsy.

Can you help me please?

Upvotes: 4

Views: 4426

Answers (2)

ealfonso
ealfonso

Reputation: 7302

A simple idea is to kill the parent process:

( sleep 2; echo hola; kill $$) &

Beware that if the set -e option is enabled, and the first command fails, the kill will not be executed. In this case, either disable it via set +e or ensure the commands before kill always succeed, e.g. append || true.

Upvotes: 0

pasaba por aqui
pasaba por aqui

Reputation: 3529

The following script monitors test child processes (in the example, sleep+false and sleep+true) and reports their PID and exit code:

#!/bin/bash

set -m

trap myhandler CHLD

myhandler() {
  echo sigchld received
  cat /tmp/foo
}

( sleep 5; false; echo "exit p1=$?" ) > /tmp/foo &
p1=$!
echo "p1=$p1"

( sleep 3; true; echo "exit p2=$?" ) > /tmp/foo &
p2=$!
echo "p2=$p2"

pstree -p $$
wait

The result is:

p1=3197
p2=3198
prueba(3196)─┬─prueba(3197)───sleep(3199)
             ├─prueba(3198)───sleep(3201)
             └─pstree(3200)
sigchld received
sigchld received
exit p2=0
sigchld received
exit p1=1

It could be interesting to use SIGUSR1 instead of SIGCHLD; see here for an example: https://stackoverflow.com/a/12751700/4886927.

Also, inside the trap handler, it is posible to verify which child is still alive. Something like:

myhandler() {
  if kill -0 $p1; then
    echo "child1 is alive"
  fi 
  if kill -0 $p2; then
    echo "child2 is alive"
  fi 
}

or kill both childs when one of them dies:

myhandler() {
  if kill -0 $p1 && kill -0 $p2; then
     echo "all childs alive"
  else
    kill -9 $p1 $p2
  fi
}

Upvotes: 3

Related Questions