According to the manual of the set builtin, 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 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 status is being inverted with
!
.
Any of the above would work, except the first since you're not running a loop.
Though for readability it's probably better to use an if
statement:
set -e
if x=$(echo a; false); then
echo "subshell worked"
else
echo "subshell failed"
fi
echo "x: $x"
Or you can invert the result:
bash -c 'set -e; ! x=$(echo a; false); echo "[$?]x:$x"' # [0]x:a
bash -c 'set -e; ! y=$(echo b; true ); echo "[$?]y:$y"' # [1]y:b
But beware that the result is now inverted. Fortunately bash doesn't exit even though a successful subshell is inverted.
Interestingly the following seems also work, by grouping the subshell invocation in a list, then invert the whole list, not just the subshell invocation:
bash -c 'set -e; ! { x=$(echo a; false); echo "[$?]x:$x"; }' # [1]x:a
This somehow matches the 3rd case above, which is also a list, except it's not using &&
or ||
.
A Special Case with Inline Initialization
Note about a tricky case with function local variables, compare the two almost identical functions below:
f() { local v=$(echo data; false); echo output:$v, status:$?; }
g() { local v; v=$(echo data; false); echo output:$v, status:$?; }
In both functions, the subshell ends with a false
command which causes it to fail, however, when executed, we'll get:
$ f # fooled by 'local' with inline initialization
output:data, status:0
$ g # good one, with separated declaration and initialization
output:data, status:1
Why?
In bash, local
is actually a builtin command. When the output of a subshell is used to initialize a local
variable, the exit status is no longer the one of the subshell, but that of the local
command, which is 0
as long as the local variable gets declared.
See also https://stackoverflow.com/a/4421282/537554