POSIX eval – Behavior Under ‘set -e’ in Conditional Expressions

evalopenbsdposix

Consider the commands

eval false || echo ok
echo also ok

Ordinarily, we'd expect this to execute the false utility and, since the exit status is non-zero, to then execute echo ok and echo also ok.

In all the POSIX-like shells I use (ksh93, zsh, bash, dash, OpenBSD ksh, and yash), this is what happens, but things get interesting if we enable set -e.

If set -e is in effect, OpenBSD's sh and ksh shells (both derived from pdksh) will terminate the script when executing the eval. No other shell does that.

POSIX says that an error in a special built-in utility (such as eval) should cause the non-interactive shell to terminate. I'm not entirely sure whether executing false constitutes "an error" (if it was, it would be independent of set -e being active).

The way to work around this seems to be to put the eval in a sub shell,

( eval false ) || echo ok
echo also ok

The question is whether I'm expected to have to do that in a POSIX-ly correct shell script, or whether it's a bug in OpenBSD's shell? Also, what is meant by "error" in the POSIX text linked to above?


Extra bit of info: The OpenBSD shells will execute the echo ok both with and without set -e in the command

eval ! true || echo ok

My original code looked like

set -e
if eval "$string"; then
    echo ok
else
    echo not ok
fi

which would not output not ok with string=false using the OpenBSD shells (it would terminate), and I wasn't sure it was by design, by mistake or by misunderstanding, or something else.

Best Answer

That no other shell needs such workaround is an strong indication that it is a bug in OpenBSD ksh. In fact, ksh93 doesn't show such issue.

That there is a || in the command line must avoid the shell exit caused by an return code of 1 on the left side of it.

The error of an special built-in shall cause the exit of a non interactive shell acording to POSIX but that is not always true. Trying to continue out of a loop is an error, and continue is a builtin. But most shells do not exit on:

continue 3

A builtin that emits a clear error but doesn't exit.

So, the exit on false is generated by the set -e condition not by the builtin characteristic of the command (eval in this case).

The exact conditions on which set -e shall exit are quite more fuzzy in POSIX.