Is there an elegant way to store and evaluate return values ​​in bash scripts?

I have a rather complicated series of commands in bash that returns a returning meaningful exit code. In different places in the script, it is necessary to divide conditionally on whether the set of commands has been successfully installed.

I am currently saving the exit code and checking it numerically, something like this:

long_running_command | grep -q trigger_word status=$? if [ $status -eq 0 ]; then : stuff else : more code if [ $status -eq 0 ]; then : stuff else 

For some reason, it seems like it should be easier. We have a simple exit code, and now we repeatedly type in numerical test operations to run it. For example, I can deceive the use of string output instead of a return code, which is easier to check for:

 status=$(long_running_command | grep trigger_word) if [ $status ]; then : stuff else : more code if [ $status ]; then : stuff else 

On the surface, it looks more straightforward, but I understand that it is dirty.

If the other logic were not so complicated, and I only ran it once, I understand that I can embed it instead of the test operator, but this is not ideal when you need to reuse the results in other places without restarting the test :

 if long_running_command | grep -q trigger_word; then : stuff else 

The only thing I have found so far is code assignment as part of command substitution:

 status=$(long_running_command | grep -q trigger_word; echo $?) if [ $status -eq 0 ]; then : stuff else 

Even this is technically not a one-time assignment (although some may argue that readability is better), but the necessary numerical testing syntax still seems cumbersome to me. Maybe I'm just an OCD.

Am I missing a more elegant way to assign an exit code to a variable and then step into it later?

+6
source share
6 answers

A simple solution:

 output=$(complex_command) status=$? if (( status == 0 )); then : stuff with "$output" fi : more code if (( status == 0 )); then : stuff with "$output" fi 

Or more eleganter-ish

 do_complex_command () { # side effects: global variables # store the output in $g_output and the status in $g_status g_output=$( command -args | commands | grep -q trigger_word ) g_status=$? } complex_command_succeeded () { test $g_status -eq 0 } complex_command_output () { echo "$g_output" } do_complex_command if complex_command_succeeded; then : stuff with "$(complex_command_output)" fi : more code if complex_command_succeeded; then : stuff with "$(complex_command_output)" fi 

Or

 do_complex_command () { # side effects: global variables # store the output in $g_output and the status in $g_status g_output=$( command -args | commands ) g_status=$? } complex_command_output () { echo "$g_output" } complex_command_contains_keyword () { complex_command_output | grep -q "$1" } if complex_command_contains_keyword "trigger_word"; then : stuff with "$(complex_command_output)" fi 
+3
source

Why don't you set flags for things that should happen later?

 cheeseballs=false nachos=false guppies=false command case $? in 42) cheeseballs=true ;; 17 | 31) cheeseballs=true; nachos=true; guppies=true;; 66) guppies=true; echo "Bingo!";; esac $cheeseballs && java -crash -burn $nachos && python ./tex.py --mex if $guppies; then aquarium --light=blue --door=hidden --decor=squid else echo SRY fi 
+2
source

Based on the clarification of the OP that it relates only to success v. failure (unlike specific exit codes):

 long_running_command | grep -q trigger_word || failed=1 if ((!failed)); then : stuff else : more code if ((!failed)); then : stuff else 
  • Sets the success indicator variable only on error (through || , that is, if a nonzero exit code is returned).
  • It is based on the fact that variables that are not defined evaluate to false in the arithmetic conditional (( ... )) .
  • Care must be taken that the variable ( $failed in this example) is not accidentally initialized elsewhere.

(On the side of the note, as mentioned in the @nos comment, you need to be careful with the pipeline related commands from man bash (my highlight):

The pipeline return status is the exit status of the last command if the pipefail option is not enabled. If the abandonment function is associated with the Pipeline Return Status, this is the value of the last (rightmost) command to exit with a non-zero status or zero if all commands have completed successfully.

To install pipefail (which is disabled by default), use set -o pipefail ; to undo it, use set +o pipefail .)

+2
source

If you don’t need to save a specific exit status, is the command just successful or unsuccessful (for example, if grep match is found), I use fake boolean variable to save the result:

 if long_running_command | grep trigger_word; then found_trigger=true else found_trigger=false fi # ...later... if ! $found_trigger; then # stuff to do if the trigger word WASN'T found fi #... if $found_trigger; then # stuff to do if the trigger WAS found fi 

Notes:

  • There really are no logical (true / false) variables in the shell. What actually happens here is that "true" and "false" are stored as strings in the found_trigger variable; when if $found_trigger; then if $found_trigger; then execute, it runs the value of $found_trigger as a command, and it happens that the true command always executes, and the false command always fails, which leads to the β€œright” thing. In if ! $found_trigger; then if ! $found_trigger; then if ! $found_trigger; then value "!" Toggles success / failure status, effectively acting as a logical "not."
  • if long_running_command | grep trigger_word; then if long_running_command | grep trigger_word; then equivalent to running the command, then using if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then to check its exit status. I think this is a little cleaner, but you should get used to thinking about if as a test of success / failure of a team, and not just testing Boolean conditions. If the "active" if commands are not intuitive to you, use a separate test instead.
+1
source

If you do not need the exact error code, you can do:

 if long_running_command | grep -q trigger_word; then success=1 : success else success=0 : failure fi if ((success)); then : success else : failure fi 

Using 0 for false and 1 for true is my preferred way of storing booleans in scripts. if ((flag)) mimics C.

If you care about the exit code, you can do:

 if long_running_command | grep -q trigger_word; then status=0 : success else status=$? : failure fi if ((status == 0)); then : success else : failure fi 

I prefer an explicit test against 0 instead of using ! which is not read correctly.

(And yes, $? Gives the correct value here.)

0
source

Hmm, the problem is a bit vague - if possible, I suggest considering the possibility of reorganization / simplification, i.e.

 function check_your_codes { # ... run all 'checks' and store the results in an array } ### function process_results { # do your 'stuff' based on array values } ### create_My_array check_your_codes process_results 

Also, if you really don't need to save the exit code, then there is no need for store_and_test - just test_and_do , i.e. use the case statement as suggested above, or something like

 run_some_commands_and_return_EXIT_CODE_FROM_THE_LAST_ONE if [[ $? -eq 0 ]] ; then do_stuff else do_other_stuff ; fi :) Dale 
-1
source

All Articles