ANDing exit codes in bash

I have a bash script that runs three checks over my source code, and then exit 0 if all the commands succeeded, or exit 1 if any of them failed:

#!/bin/bash

test1 ./src/ --test-1=option
exit_1=$?

test2 ./src/ test-2-options
exit_2=$?

test3 ./src/ -t 3 -o options
exit_3=$?

# Exit with error if any of the above failed
[[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]]
exit $?

This code works, but it feels overly long and verbose. Is there some way this can be made nicer? Specifically I am not happy with:

  • Having to run the command, and then assign the exit code to a variable
  • Having to use [[ ... ]], then collect its exit code on the next line to exit with
  • Having to explicitly compare variables to 0, as in [[ $var -eq 0 ]], instead of treating them as booleans

Ideally, the end result would be something more readable like:

exit_1=( test1 ./src/ --test-1=option )
exit_2=( test2 ./src/ test-2-options )
exit_3=( test3 ./src/ -t 3 -o options )

# Exit with error if any of the above failed
exit ( $exit_1 && $exit_2 && $exit_3 )

Some things I have considered:


Getting the error code in to a variable in one line:

exit_1=$( test1 ./src/ --test-1=option )$?
exit_2=$( test2 ./src/ test-2-options )$?
exit_3=$( test3 ./src/ -t 3 -o options )$?

This works, and makes this bit shorter, but I've never seen anyone else use this before. Is this a sensible/sane thing to do? Are there any issues with this?


Just running the tests, and && them together:

test1 ./src/ --test-1=option && \
test2 ./src/ test-2-options && \
test3 ./src/ -t 3 -o options
status=$?

This does not work, as bash short circuits. If test1 fails, test2 and test3 do not run, and I want them all to run.


Detecing errors and exiting using || exit

[[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] || exit 1

This saves one line of awkward exit codes and variables, but the important bit of exit 1 is now right at the end of the line where you can miss it. Ideally, something like this would work:

exit [[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]]

Of course, this does not work, as [[ returns its output instead of echoing it.

exit $( [[ $exit_1 -eq 0 && $exit_2 -eq 0 && $exit_3 -eq 0 ]] ; echo $? )

does work, but still seems like a horrid cludge


Not explicitly dealing with exit-codes-as-boolean

[[ $exit_1 && $exit_2 && $exit_3 ]]

This does not do what you would hope it would do. The easiest way of && together three return codes stored in variables is with the full $var -eq 0 && .... Surely there is a nicer way?


I know bash is not a nice programming language - if you can even call it that - but is there any way I can make this less awkward?

Answers


You can use bash's arithmetic command to OR the exit codes together, and negate the result, to get an exit code of 1 if any of the codes is non-zero. First, an example:

$ ! (( 0 | 0 | 0 )); echo $?
0
$ ! (( 1 | 0 | 0 )); echo $?
1

Now, your script:

#!/bin/bash

test1 ./src/ --test-1=option; exit_1=$?
test2 ./src/ test-2-options;  exit_2=$?   
test3 ./src/ -t 3 -o options; exit_3=$?

# Exit with error if any of the above failed. No need for a final
# call to exit, if this is the last command in the script
! (( $exit_1 || $exit_2 || $exit_3 ))

Or in general, you can accumulate the exit codes as you run an arbitrary number of tests:

#!/bin/bash

# Unfortunately, ||= is not an assignment operator in bash.
# You could use |=, I suppose; you may not be able to assign
# any meaning to any particular non-zero value, though.
test1 ./src/ --test-1=option; (( exit_status = exit_status || $? ))
test2 ./src/ test-2-options;  (( exit_status = exit_status || $? ))  
test3 ./src/ -t 3 -o options; (( exit_status = exit_status || $? ))
# ...
testn ./src "${final_option_list[@]}"; (( exit_status = exit_status || $? ))

exit $exit_status   # 0 if they all succeeded, 1 if any failed

Need Your Help

Using returned EitherT in haskell program

haskell monads monad-transformers either io-monad

I'm trying to use the "citation-resolve" package in a Haskell project I'm working on, but I'm having trouble getting my head around using EitherT's in real code. I get that they're monad transforme...

Using vb.net dll in unmanaged c++ project

c++ vb.net com dll interop

I created a vb.net dll called "WSdll.dll".