Execute multiple commands in a bash script sequentially and fail if at least one of them fails

Bash

Bash Problem Overview


I have a bash script that I use to execute multiple commands in sequence and I need to return non-zero exit code if at least one command in the sequence returns non-zero exit code. I know there is a wait command for that but I'm not sure I understand how to use it.

UPD The script looks like this:

#!/bin/bash
command1
command2
command3

All the commands run in foreground. All the commands need to run regardless of which exit status the previous command returned (so it must not behave as "exit on first error"). Basically I need to gather all the exit statuses and return global exit status accordingly.

Bash Solutions


Solution 1 - Bash

Just do it:

EXIT_STATUS=0
command1 || EXIT_STATUS=$?
command2 || EXIT_STATUS=$?
command3 || EXIT_STATUS=$?
exit $EXIT_STATUS

Not sure which of the statuses it should return if several of commands have failed.

Solution 2 - Bash

If by sequence you mean pipe then you need to set pipefail in your script like set -o pipefail. From man bash:

> The return status of a pipeline is the exit status of the last command, > unless the pipefail option is > enabled. If pipefail is enabled, the pipeline's return status is the value of the last (rightmost) > command to exit with a non-zero status, or zero if all commands exit successfully. If the reserved > word ! precedes a pipeline, the exit status of that pipeline is the logical negation of the exit > status as described above. The shell waits for all commands in the pipeline to terminate before > returning a value.

If you just mean sequential commands then just check the exit status of each command and set a flag if the exit status is none zero. Have your script return the value of the flag like:

#!/bin/bash

EXIT=0
grep -q A <<< 'ABC' || EXIT=$?  # Will exit with 0
grep -q a <<< 'ABC' || EXIT=$?  # Will exit with 1
grep -q A <<< 'ABC' || EXIT=$?  # Will exit with 0
echo $EXIT                      # Will print 1
exit $EXIT                      # Exit status of script will be 1 
 

This uses the logical operator OR || to only set EXIT if the command fails. If multiple commands fail the exit status from the last failed command will be return by the script.

If these commands are not running in the background then wait isn't relevant here.

Solution 3 - Bash

If you wish to know which command failed, but not neccessarily its return code you could use:

#!/bin/bash

rc=0;
counter=0;

command1 || let "rc += 1 << $counter"; let counter+=1;
command2 || let "rc += 1 << $counter"; let counter+=1;
command3 || let "rc += 1 << $counter"; let counter+=1;

exit $rc

This uses bit shifting in bash in order to set the bit corresponding to which command failed.

Hence if the first command failed you'll get an return code of 1 (=2^0), if the third failed you would get a return code of 8 (=2^3), and if both the first and the third command failed you would get 9 as the return code.

Solution 4 - Bash

If you wish to know which command failed:

#!/bin/bash
EXITCODE_RESULT=0
command1
EXIT_CODE_1=$?
command2
EXIT_CODE_2=$?
command3
EXIT_CODE_3=$?

for i in ${!EXIT_CODE_*}
do
    # check if the values of the EXIT_CODE vars contain 1
    EXITCODE_RESULT=$(($EXITCODE_RESULT || ${!i}))
    if [ ${!i} -ne 0 ]
    then
        var_fail+="'$i' "
        
    else
        var_succ+="'$i' "
    fi
done

In $var_fail you get a list of the failed EXIT_CODE vars and in $var_succ a list of the successful ones

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndrii YurchukView Question on Stackoverflow
Solution 1 - BashkanView Answer on Stackoverflow
Solution 2 - BashChris SeymourView Answer on Stackoverflow
Solution 3 - Bashimp25View Answer on Stackoverflow
Solution 4 - BashalixanderView Answer on Stackoverflow