iDev
iDev

Reputation: 2433

Get exit status of background processes in bash

I have tried multiple different ways to retrieve the exit status of a background process:

  1. Capture pid of each background process, store in an array and then wait for each PID, get return status of each PID and store in a STATUS array.

Con: pid is not a child of this shell

  1. tail --pid= -f /dev/null

Con: exit status is always 0 here

Looked around for various answers on stackoverflow. I am still not able to get it working. Can you please help and let me know where am I going wrong?

PIDS=()
STATUS=()
OVERALL_EXIT=0

# run processes and store pids in array

for target in ${target_list} ; do
    ./<script_to_execute> ${target} &
    PIDS+=$!
done


# wait for all processes to finish and then capture return status of each
for pid in ${PIDS[@]}; do
    echo "${pid}"
    wait ${pid} 
    #tail —pid=${pid} -f /dev/null
    #ps ax | grep ${pid} | grep -v grep 
    STATUS+=($?)
done

# looping through the status arr to check exit code for each
i=0
for st in ${STATUS[@]}; do
    if [[ ${st} -ne 0 ]]; then
        echo "$i failed"
        OVERALL_EXIT=1
    else
        echo "$i finished"
    fi
    ((i+=1))
done

exit ${overall_exit}

Upvotes: 2

Views: 5643

Answers (2)

Ole Tange
Ole Tange

Reputation: 33748

(This should have been a comment, but code gets formatted wrongly).

Looking at your full code it seems you are trying to implement a basic version of GNU Parallel in bash.

parallel -j0 ./<script_to_execute> ::: ${target_list}

I believe this will do the same as the full code (i.e. return an error if one of the jobs failed).

Upvotes: 2

Charles Duffy
Charles Duffy

Reputation: 295815

PIDS+=$!

...doesn't do what you think it does. Consider:

PIDS=( )
PIDS+=11
PIDS+=22
PIDS+=33
declare -p PIDS

...if what you expect this to output is:

declare -a PIDS='([0]="11" [1]="22" [2]="33")

...you'd be mistaken, because what it actually emits is:

declare -a PIDS='([0]="112233")'

...because += only appends a new array element when the thing on the right-hand side is an array.

Thus, you get a not a child of this shell error because the result of concatenating all your PIDs together into a single string isn't a PID that actually exists.

To fix it, use parens: PIDS+=( "$!" )


To provide an end-to-end example:

#!/usr/bin/env bash

# run four different processes; two exit status 0, one exits status 1, on exits status 2
# ...exits happen at delays ranging between 2-5 seconds.
delays=( 5 3 2 4 )
exits=(  0 0 1 2 )
for idx in "${!delays[@]}"; do
  (sleep "${delays[$idx]}"; exit "${exits[$idx]}") &
  pids+=( "$!" )
done

exit_status=0
for pid in "${pids[@]}"; do
  wait "$pid"; (( exit_status |= $? ))
done
echo "Combined exit status is $exit_status"
exit "$exit_status"

...properly exits after 5 seconds with:

Combined exit status is 3

Upvotes: 5

Related Questions