- Treat unset variables and parameters other than the special parameters
"*" as an error when performing parameter expansion. If expansion is attempted on an unset variable or parameter, the shell prints an error message, and, if not
-interactive, exits with a nonzero status.
POSIX states that, in the event of an expansion error, a non-interactive shell shall exit when the expansion is associated with either a shell special builtin (which is a distinction
bash regularly ignores anyway, and so maybe is irrelevant) or any other utility besides.
- Consequences of Shell Errors:
- An expansion error is one that occurs when the shell expansions defined in Word Expansions are carried out (for example,
! is not a valid operator); an implementation may treat these as syntax errors if it is able to detect them during tokenization, rather than during expansion.
- [A]n interactive shell shall write a diagnostic message to standard error without exiting.
trap ... ERR
- 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
- ...part of the test in an
- ...part of a command executed in a
|| list except the command following the final
- ...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
Note above that the ERR trap is all about the evaluation of some other command's return. But when an expansion error occurs, there is no command run to return anything. In your example,
echo never happens - because while the shell evaluates and expands its arguments it encounters an
-unset variable, which has been specified by explicit shell option to cause an immediate exit from the current, scripted shell.
And so the EXIT trap, if any, is executed, and the shell exits with a diagnostic message and exit status other than 0 - exactly as it should do.
As for the rc: 0 thing, I expect that is a version specific bug of some kind - probably to do with the two triggers for the EXIT occurring at the same time and the one getting the other's exit code (which should not occur). And anyway, with an up-to-date
bash binary as installed by
printf "shell options:\t$-\n"
trap 'echo "EXIT (rc: $?)"' EXIT
I added the first line so you can see that the shell's conditions are those of a scripted shell - it is not interactive. The output is:
shell options: hB
bash: line 4: UNSET_VAR: unbound variable
EXIT (rc: 1)
Here are some relevant notes from recent changelogs:
- Fixed a bug that caused asynchronous commands to not set
- Fixed a bug that caused error messages generated by expansion errors in
for commands to have the wrong line number.
- Fixed a bug that caused SIGINT and SIGQUIT to not be
trappable in asynchronous subshell commands.
- Fixed a problem with interrupt handling that caused a second and subsequent SIGINT to be ignored by interactive shells.
- The shell no longer blocks receipt of signals while running
trap handlers for those signals, and allows most
trap handlers to be run recursively
trap handlers while a
trap handler is executing).
I think it is either the last or the first that is most relevant - or possibly a combination of the two. A
trap handler is by its very nature asynchronous because its whole job is to wait for and handle asynchronous signals. And you trigger two simultaneously with
And so maybe you should just update, but if you like yourself, you'll do it with a different shell altogether.