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.

Reply via email to