3

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?

marmistrz
  • 5,974
  • 10
  • 42
  • 94
  • 1
    trap SIGCHLD, it allows execute some statement at child end – pasaba por aqui May 28 '16 at 08:42
  • Can you use [Expect](http://expect.sourceforge.net/)? Trapping exiting subprocesses is more elegant in Expect than POSIX shells or Bash. – nwk May 28 '16 at 13:46
  • @nwk I'm expected to write a bash script. So I doubt this will be allowed. – marmistrz May 28 '16 at 14:29
  • @pasabaporaqui whatever I try with trapping `SIGCHLD` comes far from what I described in here. Either the process doesn't exit at all (and doesn't react to SIGINT) or exits prematurely. – marmistrz May 28 '16 at 14:30

2 Answers2

3

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
}
Community
  • 1
  • 1
pasaba por aqui
  • 3,446
  • 16
  • 40
  • This will wait for both processes to exit. If the second one were `( yes; echo "exit p2=$?" ) > /tmp/foo &`, then our script won't stop after the `false` command has stopped. – marmistrz May 28 '16 at 17:10
  • @marmistrz: Of course, now is your time to add the "kill" commands or whatever you need to stop the other childs. – pasaba por aqui May 28 '16 at 19:07
  • Ok, I shouldn't have focused on the `wait`. Why are there more `SIGCHLD`s than the backgrounded processes? And why is sigchld received before the command ends? – marmistrz May 28 '16 at 20:43
  • 1
    @marmistrz: This is the problem with sigchld, any command used by the script triggers one. For this reason, the suggestion to use sigusr1. I've will edit the answer for an alternative. – pasaba por aqui May 29 '16 at 09:19
0

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.

ealfonso
  • 6,622
  • 5
  • 39
  • 67