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

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.


Solution 1:

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:

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:

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.