Exit a bash script if an error occurs in it or any of the background jobs it creates [duplicate] Exit a bash script if an error occurs in it or any of the background jobs it creates [duplicate] shell shell

Exit a bash script if an error occurs in it or any of the background jobs it creates [duplicate]


Collect the PIDs for the background jobs; then, use wait to collect the exit status of each, exiting the first time any PID polled over in that loop is nonzero.

install_pids=( )for dir in ./projects/**/; do  (cd "$dir" && exec npm install) & install_pids+=( $! )donefor pid in "${install_pids[@]}"; do  wait "$pid" || exitdone

The above, while simple, has a caveat: If an item late in the list exits nonzero prior to items earlier in the list, this won't be observed until that point in the list is polled. To work around this caveat, you can repeatedly iterate through the entire list:

install_pids=( )for dir in ./projects/**/; do  (cd "$dir" && exec npm install) & install_pids+=( $! )donewhile (( ${#install_pids[@]} )); do  for pid_idx in "${!install_pids[@]}"; do    pid=${install_pids[$pid_idx]}    if ! kill -0 "$pid" 2>/dev/null; then # kill -0 checks for process existance      # we know this pid has exited; retrieve its exit status      wait "$pid" || exit      unset "install_pids[$pid_idx]"    fi  done  sleep 1 # in bash, consider a shorter non-integer interval, ie. 0.2done

However, because this polls, it incurs extra overhead. This can be avoided by trapping SIGCHLD and referring to jobs -n (to get a list of jobs whose status changed since prior poll) when the trap is triggered.


Bash isn't made for parallel processing such as this. To accomplish what you want, I had to write a function library. I'd suggest seeking a language more readily suited to this if possible.

The problem with looping through the pids, such as this...

#!/bin/bashpids=()f() {   sleep $1   echo "no good"   false}t() {   sleep $1   echo "good"   true}t 3 &pids+=$!f 1 &pids+=$!t 2 &pids+=$!for p in ${pids[@]}; do   wait $p || echo faileddone

The problem is that "wait" will wait on the first pid, and if the other pids finish before the first one does, you'll not catch the exit code. The code above shows this problem on bash v4.2.46. The false command should produce output that never gets caught.