Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sourcing a bash script does not error when pipelined with &&

Tags:

bash

I'm finding that sourcing the following bash script does not cause the a sequence of commands to stop when pipelined with &&.

sourceme.sh:

#!/usr/bin/env bash
set -o errexit
set -o | grep errexit

echo "About to error from sourceme.sh..."
$(false)

echo "sourceme.sh did not exit on error (exit code: $?)"

This is the call to demonstrate the problem.

(source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")

The output I get from this is as follows.

errexit         on
About to error from sourceme.sh...
sourceme.sh did not exit on error (exit code: 1)
sourceme.sh did not error (exit code: 0)

This is the opposite of what I expected, where I expected to see the the whole command die when $(false) is reached in sourceme.sh, producing the following outout.

errexit         on
About to error from sourceme.sh...

Continuing the bizarreness, if I use ; in place of &&, then I would expect the command continue, but this is not what happens.

$ (source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
errexit         on
About to error from sourceme.sh...

So when sourcing the file && behaves as I expect ; to behave, and ; behaves as I would expect to && to behave.

Where am I going wrong with my understanding of how this should work?

like image 555
Courtney Miles Avatar asked Dec 13 '25 18:12

Courtney Miles


2 Answers

The idea, roughly stated, is that set -o errexit (or, equivalently, set -e) causes the shell to exit on uncaught errors. The fact that && follows source sourceme.sh means that the error is caught.

So, if the source and echo commands are connected with ;, the shell exits:

$ (source sourceme.sh ; echo "sourceme.sh did not error (exit code: $?)")
errexit         on
About to error from sourceme.sh...

But, if the source and echo commands are connected with &&, then the shell assumes that you have anticipated the possibility of the source command failing and the shell does not exit:

$ (source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")
errexit         on
About to error from sourceme.sh...
sourceme.sh did not exit on error (exit code: 1)
sourceme.sh did not error (exit code: 0)

Much of the behavior of set -e may be considered odd or unexpected. For more examples, see Why doesn't set -e (or set -o errexit, or trap ERR) do what I expected?.

Documentation

The behavior of set -e (errexit) is defined more precisely in man bash:

-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.

If a compound command or shell function executes in a context where -e is being ignored, none of the commands executed within the compound command or function body will be affected by the -e setting, even if -e is set and a command returns a failure status. If a
compound command or shell function sets -e while executing in a context where -e is ignored, that setting will not have any effect until the compound command or the command containing the function call completes.

Synonyms

As documented in man bash as well as in help set, the following two commands are equivalent:

set -e
set -o errexit
like image 54
John1024 Avatar answered Dec 16 '25 10:12

John1024


What you are observing is this.

This command doesn't exit on error:

(source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")

However this command:

(bash sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")

does exit so && is not the only reason of this weirdness. It is combination of source and && that makes it not to exit.

You have also noted that use of ; instead of && makes it exit as in following command:

(bash sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")

This behaviour is actually dependent of bash version in use.

When I run this command on OSX bash 3.2.57(1)-release it does exit:

(source sourceme.sh && echo "sourceme.sh did not error (exit code: $?)")

errexit         on
About to error from sourceme.sh...

However on bash versions 4.2 and above behaviour changes and set -e doesn't exit when used with &&.

So bottomline is that set -e is highly unreliable and one should avoid depending on it's behaviour in important shell scripts.

like image 44
anubhava Avatar answered Dec 16 '25 11:12

anubhava



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!