'Why doesn't bash flag -e exit when a subshell fails?

I'm a bit confused here. My goal here is to have the bash script exit with a non-zero exit code when any of the commands within the script fails. Using the -e flag, I assumed this would be the case, even when using subshells. Below is a simplified example:

#!/bin/bash -e

(false)

echo $?
echo "Line reached!"

Here is the output when ran:

[$]>Tests/Exec/continuous-integration.sh 
1
Line reached!

Bash version: 3.2.25 on CentOS



Solution 1:[1]

I have seen this behavior in bash version 3.2.51 on both SuSE 11.3 and Mac OS prior to El Capitan. Bash 3.2.57 on El Capitan has the "correct" behavior, ie like bash 4.

However, the workaround proposed above, adding "|| exit $?" after the subshell's closing paren, defeats the intent of the -e flag no matter what version of bash. From man bash:

-e Exit immediately if a simple 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 in an if statement, part of a && or || list, ...

A subshell followed by "|| exit $?" apparently counts as a command list; and the bash -e flag will not apply to ANY command inside the subshell. Try it:

$ set -e
$ ( echo before the error; false; echo after the error, status $?; ) || echo after the subshell, status $?
before the error
after the error, status 1
$

Because the subshell is followed by ||, that "echo after the error" is run, even with set -e. Not only that, the subshell exits 0, because that "echo" ran. So "|| exit $?" would not even run the "exit". Probably not what we wanted!

So far as I know, the following formula is compatible with bash versions whether they honor bash -e after subshell, or not. It even behaves correctly if the -e flag happens to be reset:

Add the following line immediately after the closing parenthesis of every subshell in the bash script:

case $?/$- in ( 0/* ) ;; ( */*e* ) exit $? ;; esac # honor bash -e flag when subshell returns

Solution 2:[2]

The -e option is for current shell, for sub-shell, functions, etc we use -E

form man bash

-E If set, any trap on ERR is inherited by shell functions, command substitutions, and commands executed in a subshell environment.

And for advanced users, a kind of strict mode we have which is:

set -Eeuo pipefail
  • -E explained above
  • -e exit immediately
  • -u exit for unbound variable found
  • -o set option
  • pipefail exit if we had failure on a pipe

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Shakiba Moshiri