Hi Bart, I just noticed the following change: +Changes since 5.9 > +----------------- > + > +Handling of ERR_EXIT is corrected > *when the final status of a structured+command* (for, select, while, > repeat, if, case, or a list in braces) > *is+nonzero*. To be compatible with other shells, *"zsh -e" now exits* in > +those circumstances, whereas previous versions did not. This does not > +affect the handling of nonzero status within conditional statements. This looks wrong to me. It's not compatible with the third exception of POSiX "set -e" : When this option is on, when any command fails (for any of the reasons > listed in 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 > 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.* > > My high-level understanding of exception 3 is that if an error was produced by a condition (note that the last command in a sub-list is NOT a condition but a regular command), then that error should bubble up the evaluation stack, without triggering any ERR_EXIT, until it gets ignored (for example on the left of a ";" in a sequence) or it becomes the result of an enclosing function or subshell. Let's consider the following example: function foo() { if true; then false && true; fi } > function bar() { foo } > bar Exception 2 tells us that "false && true" shouldn't trigger an ERR_EXIT. By exception 3, the resulting exit status becomes the result, first of the enclosing if, and then of the enclosing {}, without triggering an ERR_EXIT. It's only in function "bar" that an "ERR_EXIT" should be triggered because the call to "foo" returns 1 and function calls aren't compound commands. Zsh 5.8.1, doesn't trigger any ERR_EXIT for this example. Zsh 5.9.*, with your latest changes, triggers an ERR_EXIT in "foo". Bash 5.1.16 triggers an ERR_EXIT in "bar". My understanding is that Bash is right. Here is the script that I used to test this behavior with Zsh and Bash (for bash you have to comment out the "foo?3" tests): # test.zsh > # Usage: test.zsh A1|...|A4|B1|...|B4|C1|...|C4 > # > # Example: "test.zsh A1" runs the test for "fooA1". > set -e > function err-exit() { > local error=$?; > # Print where ERR_EXIT was called from. > if [[ -n $ZSH_VERSION ]]; then > local file=${funcfiletrace[1]%:*} > local line=${funcfiletrace[1]#*:} > elif [[ -n $BASH_VERSION ]]; then > local caller=$(caller 0); > local file=${caller##* } > local line=${caller%% *} > fi > echo "Caught error $error at $file:$line: $(cat $file | head -$line | > tail -1)" >&2 > return $error; > } > trap err-exit ERR > if [[ -n $ZSH_VERSION ]]; then > alias init='' > elif [[ -n $BASH_VERSION ]]; then > # It looks like in Bash the ERR trap must be set in every function > shopt -s expand_aliases > alias init='trap err-exit ERR' > fi > function fooA1() { init; false ; > } > function fooA2() { init; if true; then false ; fi > } > function fooA3() { init; { false ; } always { true; } > } > function fooA4() { init; { false ; } > } > function fooB1() { init; false && true; > } > function fooB2() { init; if true; then false && true; fi > } > function fooB3() { init; { false && true; } always { true; } > } > function fooB4() { init; { false && true; } > } > function fooC1() { init; false && true ; > echo foo; } > function fooC2() { init; if true; then false && true; fi ; > echo foo; } > function fooC3() { init; { false && true; } always { true; }; > echo foo; } > function fooC4() { init; { false && true; } ; > echo foo; } > function bar() { > init > echo Start > foo$1 > echo End > } > bar "$@" I included the "foo?3" tests because all the compound commands in loop.c seem to behave the same except for "exectry", which lacks the following line: this_noerrexit = (WC_SUBLIST_TYPE(*end) != WC_SUBLIST_END); I included the "foo?4" tests because sequences are not handled by one of the functions in loop.c but by some function in exec.c. However, all "foo?3" and "foo?4" tests nevertheless behave the same as the matching "foo?2" tests. Here are the results: Zsh 5.8.1 Zsh 5.9.* Bash 5.1.16 fooA1 Exit in foo* Exit in foo* Exit in foo* > fooA2 Exit in foo* Exit in foo* Exit in foo* fooB1 Exit in bar Exit in bar Exit in bar > fooB2 No exit Exit in foo* Exit in bar fooC1 No exit No exit No exit > fooC2 No exit Exit in foo* No exit My understanding is that Bash's behavior is the correct one. Philippe