AND exit codes in bash

I have a bash script that runs three checks of 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 too long and verbose. Is there any way to make this better? In particular, I am not satisfied:

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

Ideally, the end result will be more readable, for example:

 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 reviewed:


Getting the error code in 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 )$? 

It works and makes this bit shorter, but I have never seen anyone else use this before. Is this a reasonable / normal thing? Are there any problems with this?


Just run 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 like a bash short circuit. If test1 does not work, test2 and test3 do not start, and I want them all to start.


Error Detection and Exit Using || exit || 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 exit 1 bit is now right at the end of the line where you can skip it. Ideally, something like this would work:

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

Of course, this will not work, because [[ returns its output instead of an echo.

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

really works, but still seems awful.


Not directly related to exit-codes-as-boolean

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

It does not do what you would hope to do. The easiest way to && together the three return codes stored in variables is to have the full $var -eq 0 && ... Of course, is there a better way?


I know that bash is not a good programming language - if you can even name it - but is there any way to make this less inconvenient?

+6
source share
5 answers

You can use the bash arithmetic command to OR exit codes together and deny the result to get the exit code from 1 if any of the codes is nonzero. 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, as a rule, you can accumulate exit codes when performing 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 
+7
source

Some improvements

 [ $exit_1$exit_2$exit3 = 000 ] # no exit needed here, script exits with code from last command 
+2
source

I searched for the answer to this question myself and decided in a similar way to @chepner, but did not use bash arithmetic expressions:

 #!/bin/bash failed=0 test1 ./src/ --test-1=option || failed=1 test2 ./src/ test-2-options || failed=1 test3 ./src/ -t 3 -o options || failed=1 # Exit with error if any of the above failed if [ "$failed" -ne 0 ] ; then exit fi 

You simply set the flag at the beginning and set the flag to 1 if any of the statements fails. Here || - the key.

+1
source

You can assign a command exit code using the following line:

 RES1=$(CMD > /dev/null)$? 

Example:

 RES1=$(test1 ./src/ --test-1=option > /dev/null )$? 

So your code will be:

 exit_1=$( test1 ./src/ --test-1=option > /dev/null )$? exit_2=$( test2 ./src/ test-2-options > /dev/null )$? exit_3=$( test3 ./src/ -t 3 -o options > /dev/null )$? # Exit with error if any of the above failed exit ( $exit_1 && $exit_2 && $exit_3 ) 
0
source

Why do you need three different variables?

 fn() { local -i st test1 ./src/ --test-1=option st=$? test2 ./src/ test-2-options (( st = ($? || st) )) # Use arithmetic expression to or the values at each step test3 ./src/ -t 3 -o options (( 0 == ($? || st) )) } 
0
source

Source: https://habr.com/ru/post/944200/


All Articles