Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't set -e cause a failure with `false || false && true`? [duplicate]

Tags:

linux

bash

shell

Can't figure out a fitting title, I don't understand the behavior in dash/bash. Namely I am using set -e to bail out if a command fails, and command groups to handle the positive result.

ie. the general scheme is:

[ ! wantcommand ] || command

Than means the command only gets executed if needed, and a failure will automatically kill the script.

There might be some postprocessing necessary, in that case I use this:

[ ! wantcommand ] || { command && postprocess; }

This has led to some curious bughunting, as this wont kill the shell and I cant get behind the reason. I have to go through some chunks of shell code now, but would like to understand the reason.

for testing:

bash -c 'set -e; { false || false && echo "post" ; }; echo "ec $?"'

or:

bash -c 'set -e; { set -e; false || false && echo "post" ; }; echo "ec $?"'

Note: I am not asking for a fix, but primary why the returncode is 1, but the shell wont quit

like image 876
Norbert Lange Avatar asked Dec 14 '22 02:12

Norbert Lange


1 Answers

set -e only bails on unchecked failures.

When you branch on a failure (using if, until, while, && or ||), that failure is checked.

If the specification were not written in this manner, short-circuiting boolean operations could not effectively be used for flow control because the false branches would always cause an exit.


To quote from the specification, with emphasis added:

When this option is on, when any command fails (for any of the reasons listed in Consequences of Shell Errors or by returning an exit status greater than zero), the shell immediately shall exit, as if by executing the exit special built-in utility with no arguments, with the following exceptions:

  1. The failure of any individual command in a multi-command pipeline shall not cause the shell to exit. Only the failure of the pipeline itself shall be considered.

  2. The -e setting shall be ignored when executing the compound list following the while, until, if, or elif reserved word, a pipeline beginning with the ! reserved word, or any command of an AND-OR list other than the last.

  3. If the exit status of a compound command other than a subshell command was the result of a failure while -e was being ignored, then -e shall not apply to this command.

This requirement applies to the shell environment and each subshell environment separately. For example, in:

set -e; (false; echo one) | cat; echo two

the false command causes the subshell to exit without executing echo one; however, echo two is executed because the exit status of the pipeline (false; echo one) | cat is zero.

Note that this specification has changed over time; shells implementing a prior revision of the POSIX specification may not precisely comply with the version quoted here.


To inject some opinion here -- I'd strongly suggest reading BashFAQ #105 and ensuring that you fully understand all behaviors described therein before making the decision to use set -e rather than implementing explicit error-handling by hand. The FVUE wiki further describes the distinctions in behavior between set -e in bash-native mode and POSIX mode, which should likewise be understood.

like image 174
Charles Duffy Avatar answered Feb 13 '23 23:02

Charles Duffy