3

I just started learning bash and I was reading about the $? variable. From what I understood, $? is assigned to the exit status of the last command executed.

For example

$ false; echo $?

Will yield 1, and

$ false; :; echo $?

Will yield 0

Things get a bit more complicated when I combine these with if/for blocks. The man page reads:

for NAME [in WORDS ... ] ; do COMMANDS; done

Execute commands for each member in a list.

The for loop executes a sequence of commands for each member in a list of items. If in WORDS ...; is not present, then in "$@" is assumed. For each element in WORDS, NAME is set to that element, and the COMMANDS are executed.

Exit Status:

Returns the status of the last command executed.

That means:

$ false; for i in 1 2 3; do false; done; echo $?

Will yield 1 since the last command executed is false. But

$ false; for i in; do false; done; echo $?

or

$ false; if false; then :; fi; echo $?

Will yield 0 despite the fact that false was the last command executed.

My guess is the $? variable is set to 0 when entering a for/if block. Am I missing something?

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Kostas Pelelis
  • 1,322
  • 9
  • 11

3 Answers3

4

According to bash's manual:

if list ; then list ; [ elif list ; then list ; ] ... [ else list ; ] fi

The iflist is executed. If its exit status is zero, the thenlist is executed. Otherwise, each eliflist is executed in turn, and if its exit status is zero, the corresponding thenlist is executed and the command completes. Otherwise, the elselist is executed, if present. The exit status (of the whole if ... fi block) is the exit status of the last command (in the then, elif or else block) executed, or zero if no condition tested true.

pynexj
  • 19,215
  • 5
  • 38
  • 56
  • Great! Thats makes a lot of sense. Could you provide a link to the refered paragraph? – Kostas Pelelis Jun 20 '17 at 09:11
  • Did not find the man page from [GNU's site](https://www.gnu.org/software/bash/manual/). Linked to Chet's page. Just update the answer. – pynexj Jun 20 '17 at 09:22
3

yes, after if ..;, for ..; or while ..; statements the exit status reset to 0.

to be clear this doesn't mean after fi; or done; because the exit status is the last command exit status.

EDIT : commands between if/elif and then doesn't affect exit status of compound statement whereas latest command executed between then/else and elif/fi sets the exit status of compound statement.

after a function call; the exit status will be the value returned by return or the exit status of last command.

with the option set -e, set -o errexit; the shell exits after a command with a <>0 exit status, except if followed by || or inside a if ..; statement.

exit status must be checked just after command exits, logical operators can be used &&, ||:

# to fail fast the process can't continue
simple_command || {
    echo "failed .."
    exit 1
}

the error handling is similar to exceptions, the question to be asked is does or how the process can continue after a command fails?

in pipeline commands the exit status is the exit status of the last command except with set -o pipefail : the exit status will be the exit status of the latest command in the pipe with exit status <>0.

Nahuel Fouilleul
  • 18,726
  • 2
  • 31
  • 36
  • 1
    To be clear, compound statements do not reset the exit status. The exit status is *defined* to be 0 if no command executes as a result of the compound statement. – chepner Jun 16 '17 at 12:15
  • @chepner, thanks for clarifying, commands failing inside `if list;` `while list;` `until list;` do not affect the exit status – Nahuel Fouilleul Jun 16 '17 at 12:24
  • after `else` keyword `echo $?;` gives the exit status of last `if` `elif` command: `if (exit 2); then :; elif (exit 3); then :; else echo $?; fi;` but when there is no `else` statement status is 0: `if (exit 2); then :; elif (exit 3); then :; fi;echo $?` – Nahuel Fouilleul Jun 16 '17 at 12:32
  • 1
    Yes, because none of the tested conditions returned true. I think the thing to emphasize is that a compound statement *itself* (and not the commands executed *inside* it) does not affect the value of `$?`, except under very specific corner cases, – chepner Jun 16 '17 at 12:42
1

Command $ false; for i in; do false; done; echo $? gives the exit status of for loop. Since it exits for-loop without errors, $? is 0. Here is a similar question which may be helpful. How to get the exit status a loop in bash

Addison
  • 121
  • 7
  • More precisely, the exit status of a loop is the exit status of the last command executed in the body, unless the loop never executes its body (as in this case, where `for i in; do` has an empty list to iterate over), in which case the exit status is defined to be 0. – chepner Jun 16 '17 at 12:13