Parent script continues when child exits with non-zero exit code

I have a script that calls another script. When the child script fails, I’d like the parent to fail as well.

In the child script child_1.sh, I have something like this:

if [ $SOME_BAD_CONDITION ] ; then
  echo "Error message...."
  exit 1
fi

In the parent, I have this:

#!/bin/bash
set -e
#...
bash ./child_1.sh
echo "continuing to next step..."
bash ./child_2.sh
bash ./child_3.sh
#...

I’ve set up my environment so that $SOME_BAD_CONDITION will always happen, and the script exits as expected and the error message does print, but the parent script continues: the message “continuing…” is printed and the next script begins executing.

I thought that having set -e would ensure that my parent script fails if any child script exists with a non-zero exit code, but that doesn’t seem to be happening. I’m not sure what I got wrong here…

bash version: 4.2.25


UPDATE:

I tried echoing $?:

bash ./child_1.sh
echo $?
echo "continuing to next step..."

The output looks like this:

Error message....
0
continuing to next step...

Why doesn’t the exit code from the child make it into the parent?


ANSWER: The original code snippet was incomplete. The exit 1 was inside a code block that was piped to tee. My attempt to post a clean, short code sample ignored this because I did not realize how significant it was (I’m still fairly new to bash scripting). See my posted answer for details.

Looks like your issue stems from how you throw the error in your child script.

From man set “if a simple command (see SHELL GRAMMAR above) exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in a if statement, part of an && or || list, or if the command’s return value is being inverted via !”

Rather than using set -e, use a trap statement and throw an error. (If you need to use the conditional exit.

See http://mywiki.wooledge.org/BashFAQ/105

Answered By: Crp

Since your child processes exit with a proper zero/non-zero exit code convention, and given that you execute them sequentially, I’d say you could just use the && operator:

#!/bin/bash

./child_1.sh
[[ $? -ne 0 ]] && exit # Exit if non-zero exit code
./child_2.sh
./child_3.sh
./child_4.sh
exit 0

Here, [[ $? -ne 0 ]] && exit acts like:

if [ $? -ne 0 ]; then # If: last exit code is non-zero
    exit
fi

If you don’t have anything to do between the calls, you could even use…

#!/bin/bash
./child_1.sh && ./child_2.sh && ./child_3.sh && ./child_4
exit $?

The && operator acts like a short-circuit AND operator, and a zero exit code is pretty much like a true boolean value. If at any time in the chain, a script exits with non-zero, the && will fail, and subsequents scripts shouldn’t be called.

Answered By: John WH Smith

So, I might have made an error in not posting enough details about the child script – I simplified it for the purpose of this question, but maybe too much. The exit statement was buried inside a code block (between { and }) which was piped to tee for logging:

{
  #...
  if [ $SOME_BAD_CONDITION ] ; then
    exit 1
  fi
  #...
} | tee -a $LOGFILE
echo "script ended at $date">>$LOGFILE

From what I understand, I always got a return code of 0 from this child script because the last echo was returning 0. I did some research and discovered $PIPESTATUS. I was able to use this to ensure that when my script tried to exit with exit code 1, it would be passed up to parent script:

{
  #...
} | tee -a $LOGFILE
EXIT_CODE=${PIPESTATUS[0]}
echo "script ended at $date">>$LOGFILE
exit $EXIT_CODE

Combined with John’s suggestion, I have a solution that seems to be working.

Categories: Answers Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.