1

The following bash script prints "This should never run!":

#!/bin/bash
set -e

func() {
    echo "Bailing out..."
    false # Should bail out because of set -e

    echo "This should never run!"
}

func || true

What's going on here?

A simple conclusion to make is that set -e is not propagated inside functions. However, this is not the case. Changing the last line (func || true) to just func causes the function to stop at the false statement as expected.

Another hypothesis is that bash will start executing the function, and on the first error (false) evaluate the rest of the line it was called on (func || true), and if that retuns 0 (true), then it continues executing the function as if nothing happened. However, replacing true with echo "Oops!" disproves that, since no "Oops!" is printed.

Is set -e somehow ignored when executing functions as part of a boolean expression?

3
  • 2
    Documented, expected behavior. set -e is full of corner cases, and in many circles considered more trouble than it's worth; see mywiki.wooledge.org/BashFAQ/105, as well as fvue.nl/wiki/Bash:_Error_handling and wiki.bash-hackers.org/scripting/obsolete -- to quote from that latter: The set -e feature generates more questions and false bug reports on the Bash mailing list than all other features combined! Commented Jul 1, 2016 at 21:17
  • ...by the way, to touch on the intent a bit -- check_something || do_something shouldn't exit if check_something fails; if it did, that would mean set -e and branching operations would be incompatible with each other. Unfortunately, the specification was sufficiently ill-thought-out that what we ended up with is a case where set -e is completely disabled if anywhere up the stack the result of an ongoing operation will be used for a branch... and since it's specified by POSIX and honored by compliant existing shells, that behavior can't be fixed without massive compatibility problems. Commented Jul 1, 2016 at 21:32
  • [Personally, I'd argue that we'd be better off if set -e and branching operations were in fact incompatible with each other, so long as it were a per-stack-frame flag -- this would mean it would need to be explicitly turned on when folks are too lazy to write || return (or || exit when outside a function) and turned back off afterwards, but at least the behavior would be consistent and obvious, and easy to audit]. Commented Jul 1, 2016 at 21:37

1 Answer 1

4

From man bash regarding set -e:

Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command (see SHELL GRAMMAR above), exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command's return value is being inverted with !. If a compound command other than a subshell returns a non-zero status because a command failed while -e was being ignored, the shell does not exit. A trap on ERR, if set, is executed before the shell exits. This option applies to the shell environment and each subshell environment separately (see COMMAND EXECUTION ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks. Is there a simple workaround? I tried running the function in a subshell (( func ) || true), however that didn't work either.
So if I understand correctly, set -e can't cause the function to "return" a non-zero value, and instead can only either cause the script to continue executing or stop the entire script altogether?
@VladimirPanteleev - Correct. set -e isn't simply || return $? after every command a function. It either allows the main script to run successfully or outright kills it. If you need control over a function's return value you'll have to manage it yourself.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.