On Thu, Sep 25, 2025 at 02:07:37 -0600, Stan Marsh wrote:
> >$ set -e ; ! true ; ! false ; type ! ; echo $BASH_VERSION ; false ; echo
> >Still here
> >! is a shell keyword
> >2.01.0(1)-release
>
> Yes, this one has always struck me as odd. Not a bug, of course,
> pretty much because, as you say, it's always been this way. I'm not
> sure I really understand *why* it is not a bug (I've heard more than
> one explanation from knowledgeable sources, but am still somewhat
> unconvinced).
set -e is not designed to be user-friendly or to conform to your
expectations of how a well-designed error handler should behave.
set -e is designed to conform to POSIX's ever-changing wordings and
interpretations as they attempt to pin down the correct language to
describe the varying implementations of this LEGACY feature.
In other words, there is an ! exception because that's how it's always
been. No more than that, and no less.
The ! exception is documented in the bash(1) man page:
If a sigspec is ERR, the command arg is executed whenever a
pipeline (which may consist of a single simple command), a list,
or a compound command returns a non-zero exit status, subject to
the following conditions. The ERR trap is not executed if the
failed command is part of the command list immediately following
a while or until keyword, part of the test in an if statement,
part of a 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 using !.
These are the same conditions obeyed by the errexit (-e) option.
And also in POSIX
<https://pubs.opengroup.org/onlinepubs/9799919799/utilities/V3_chap02.html#set>:
-e
When this option is on, when any command fails (for any of
the reasons listed in 2.8.1 Consequences of Shell Errors or by
returning an exit status greater than zero), the shell immediately
shall exit, as if by executing the exit special built-in utility
with no arguments, with the following exceptions:
1. The failure of any individual command in a multi-command
pipeline, or of any subshell environments in which command
substitution was performed during word expansion, shall not
cause the shell to exit. Only the failure of the pipeline
itself shall be considered.
2. The -e setting shall be ignored when executing the compound
list following the while, until, if, or elif reserved word,
a pipeline beginning with the ! reserved word, or any command
of an AND-OR list other than the last.
3. If the exit status of a compound command other than a
subshell command was the result of a failure while -e was
being ignored, then -e shall not apply to this command.
As I will continue to point out until I die, apparently, because nobody
ever seems to believe this until they've heard it a hundred times,
set -e is not your friend. You should not be using it in new scripts.
It's only there because POSIX requires it, to support legacy scripts
that were written to use it (and to support Makefiles).
set -e is not an automatic error handling mechanism.
set -e is not a reasonable substitute for proper error handling.
In the shell, you must write your own error handling code, just like
you would in C.
If this offends your sensibilities as a programmer who is used to modern
languages that have automatic error handling, too bad. The shell is
not a modern language. It does not have automatic error handling, in
large part because it's literally impossible for the shell to know
whether the exit status of an external command is the result of a
catastrophic error which should cause the whole script to terminate,
or a minor error that should be handled gracefully by the script, or no
error at all.
Here's an example of an external command returning a nonzero exit
status when there is no error:
hobbit:~$ /usr/bin/expr 5 + 5; echo $?
10
0
hobbit:~$ /usr/bin/expr 5 - 5; echo $?
0
1
Only you, the script writer, have the context to make the judgment about
which nonzero exit statuses should cause your whole script to die.
That's why you must actually MAKE that judgment, and write error handling
code appropriately.