Andrii Yurchuk
Andrii Yurchuk

Reputation: 3280

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.

Upvotes: 59

Views: 30412

Answers (4)

alixander
alixander

Reputation: 436

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

Upvotes: 0

imp25
imp25

Reputation: 2357

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.

Upvotes: 4

Chris Seymour
Chris Seymour

Reputation: 85883

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.

Upvotes: 11

kan
kan

Reputation: 28981

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.

Upvotes: 65

Related Questions