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.
In other words, an error produced by a condition should never trigger an ERR_EXIT unless it becomes the result of a function or of a subshell, in which case an ERR_EXIT should be triggered at the calling point of the function or subshell.
I assume that command substitutions play the same role as subshells and may turn errors from conditions into ERR_EXITs. However, I assume that they only ever do so if their result isn't ignored. So "v=$(...)" may trigger an ERR_EXIT but never ": $(...)". I added new tests for that and Bash does indeed behave like that.
Below is the new set of tests .I dropped the one for always blocks and for sequences, i.e., all the "foo?3" and "foo?4" tests because they don't differ from the "foo?2" tests. I also reordered the tests to better group together the ones that behave correctly.
function foo1AX() { init; false ; }
function foo1AY() { init; v=$(init; false ); }
function foo1AZ() { init; : $(init; false ); }
function foo1BX() { init; false && true ; }
function foo1BY() { init; v=$(init; false && true ); }
function foo1BZ() { init; : $(init; false && true ); }
function foo1CX() { init; false && true ; echo foo >&2; }
function foo1CY() { init; v=$(init; false && true ; echo foo >&2); }
function foo1CZ() { init; : $(init; false && true ; echo foo >&2); }
function foo2AX() { init; if true; then false ; fi ; }
function foo2AY() { init; v=$(init; if true; then false ; fi ); }
function foo2AZ() { init; : $(init; if true; then false ; fi ); }
function foo2BX() { init; if true; then false && true; fi ; }
function foo2BY() { init; v=$(init; if true; then false && true; fi ); }
function foo2BZ() { init; : $(init; if true; then false && true; fi ); }
function foo2CX() { init; if true; then false && true; fi; echo foo >&2; }
function foo2CY() { init; v=$(init; if true; then false && true; fi; echo foo >&2); }
function foo2CZ() { init; : $(init; if true; then false && true; fi; echo foo >&2); }
And here are the updated results:
Zsh 5.8.1 Zsh 5.9.0.1-dev Bash 5.1.16(1)-release
foo1AX Exit in foo* Exit in foo* Exit in foo*
foo1AY Exit in $() and foo* Exit in $() and foo* Exit in $() and foo*
foo1AZ Exit in $() Exit in $() Exit in $()
foo1BX Exit in bar Exit in bar Exit in bar
foo1BY Exit in foo* Exit in foo* Exit in foo*
foo1BZ No exit No exit No exit
foo1CX No exit No exit No exit
foo1CY No exit No exit No exit
foo1CZ No exit No exit No exit
foo2AX Exit in foo* Exit in foo* Exit in foo*
foo2AY Exit in $() and foo* Exit in $() and foo* Exit in $() and foo*
foo2AZ Exit in $() Exit in $() Exit in $()
foo2BX No exit Exit in foo* Exit in bar
foo2BY Exit in foo* Exit in $() and foo* Exit in foo*
foo2BZ No exit Exit in $() No exit
foo2CX No exit Exit in foo* No exit
foo2CY No exit Exit in $() and foo* No exit
foo2CZ No exit Exit in $() No exit
- Bash behaves correctly for all tests (from my understanding of the POSIX spec).
- Zsh 5.8 and 5.9 behave correctly for all "foo1??" and "foo2A?" tests.
- For all "foo2B?" and "foo2C?" tests, either Zsh 5.8 and/or Zsh 5.9 behave incorrectly.
- Command substitutions don't seem to have any problem (of their own).
- In other words, only test foo2BX and foo2CX matter. If they get fixed, I expect the other ones to be fixed too.
- The problem seems to be related to how "false && true" interacts with other constructs.
- In Zsh 5.9 the problem seems to be that compound commands now incorrectly trigger ERR_EXIT.
- In Zsh 5.8 I can't pinpoint the problem, for now.
Philippe