[lug] How does bash "set -e" work?

Lee Woodworth blug-mail at duboulder.com
Thu Jan 3 16:41:13 MST 2013


My cold-addled brain is having a difficulty reconciling the documnetation
of set -e with observed behavior for bash.

The docs for set -e describe syntactic forms where there is exceptional behavior
in the shell/script being parsed: control statement tests, pipelines
where || or && is used, etc. The docs in this part reference a subshell's
exit status, not its constituent commands.

They also say regarding subshells for set -e:
    ... This option applies to the shell environment and each
           subshell environment separately ...

$ { set -e; false; echo "A1: $?"; echo "A2: $?"; } || echo "B: $?"
A1: 1
A2: 0
-- As expected from the docs (but still magical)

$ (set -e; false; echo "A1: $?"; echo "A2: $?") || echo "B: $?"
A1: 1
A2: 0
-- Not expected if a subshell's parsing context is separate from
    the parent shell. The subshell contains none of the syntactic
    forms that trigger special behavior.

$ (exec bash -c 'set -e; false; echo "A1: $?"; echo "A2: $?"') || echo "B: $?"
B: 1
-- As expected, the parsing context was made separate from the parent shell
    [ The || didn't affect set -e, so it must be the parse-time detection of
      set -e and || that causes the change ]

So, parent syntactic-context affects set -e within subshells (event nested
ones) even though in other ways a subshell is isolated from the parent.

$ (set -e; false; (set -e; false; echo "A3: $?")) || echo "B: $?"
A3: 1

This may be consistent with the docs since the subshell section refers to
the execution environment. It isn't obvious from the docs though that the
parent's syntactic context also modifies bahavior *within* a subshell.

On 01/02/2013 06:23 PM, Jeffrey S. Haemer wrote:
> Lee,
> The example may have been confusing.
> Earlier posts in this thread showed that the " || " operator causes the
> "set -e" in the subshell to be ignored. (If it weren't ignored, then a
> "false" following it would cause the subshell to exit immediately with a
> non-zero exit status.)
> In the post you cited, Rob was asking a different question: Does "set -e"
> have *any* effect in the subshell when the parent shell follows it with "
> || "?
> His experiment answers "Yes. Looking at the environment shows that the
> *same* change occurs with or without the ' || ' " .  In fact, there are
> *no* differences in the environments with and without " || ".
> In both, the errexit flag is set, yet in one, it causes the subshell to
> exit, while in the other, the flag is ignored.
> Rob deduces from this that the " || " persuades the parent shell to
> instruct its subshell to ignore the flag, but that that instruction is not
> passed through the environment. In other words, there's got to be some
> other, out-of-band information channel to the child process.
> I agree, and add that I think it's both a great puzzle, and a sweet set of
> experiments that lead to Rob's solution.
> I'll add another hypothesis, with an experiment to rule it out: Perhaps '
> || ' tells the subshell's *false* to return a zero exit status! Easy to
> test:
> $ ( set -e; false; echo $? ) || *any-other-command*
> 1
> Nope, *false* still fails. Take out the '||', however, and there's no exit
> status reported, because the *set -e* causes an immediate termination of
> the subshell when it sees any failure.
> (But does it have something to do with "false," which is a shell builtin?
> You can answer this yourself with the command */bin/false*, a standalone
> binary.)
> I haven't looked at the bash source (or Googled, or even asked around, for
> that matter), so I can't tell you more. Rob -- who stayed home on New
> Year's Eve to read code -- may now be able to.
> HTH!
> On Wed, Jan 2, 2013 at 3:35 PM, Rob Nagler<nagler at bivio.biz>  wrote:
>>> My thinking is that for the example in the thread, all that is happening
>>> is that the compound forms have a true return status. No need for
>> side-channel
>>> communication between the the parent and child shells (set, env and sort
>>> returned true -- so -e doesn't matter).
>> I don't think so:
>> % (set -e; false)&&  echo true
>> % (set -e; false; echo false; exit 1)&&  echo true
>> false
>> % (set -e; false; echo false)&&  echo true
>> false
>> true
>> Rob
>> _______________________________________________
>> Web Page:  http://lug.boulder.co.us
>> Mailing List: http://lists.lug.boulder.co.us/mailman/listinfo/lug
>> Join us on IRC: irc.hackingsociety.org port=6667 channel=#hackingsociety
> _______________________________________________
> Web Page:  http://lug.boulder.co.us
> Mailing List: http://lists.lug.boulder.co.us/mailman/listinfo/lug
> Join us on IRC: irc.hackingsociety.org port=6667 channel=#hackingsociety

More information about the LUG mailing list