9

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.

  • eval false generates a non-zero status so I would expect set -e to terminate the script at that point. In the case of ! set -e does not apply as ! statement explicitly checks the exit status. – fcbsd Jun 12 at 8:56
  • @fcbsd Would you expect eval false to terminate the script even if it's part of an AND-OR list or a conditional statement? I wouldn't. – Kusalananda Jun 12 at 8:58
  • I'm not sure if set -e is set if that is the correct behaviour... I agree that it makes sense to not terminate in a conditional statement. – fcbsd Jun 12 at 9:14
  • having played some more, with sh on CentOS 7 - I would say that is intended behaviour for OpenBSD's ksh/sh when using set -e so the ` ( )` is the answer. – fcbsd Jun 12 at 9:59
4
+50

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.

  • This echoes the response that I got off the OpenBSD mailing list, but with more words, thanks! I'll sort out a proper bug report, and if nothing happens, I'll look at the source code myself. – Kusalananda Jun 13 at 13:24
3

[sorry if this is not a real answer, I'll update it when I get round to it]

I had a look at the source code, and my conclusions are:

1) It's a bug/limitation, nothing philosophical behind it.

2) The "fix" for it from the portable fork of OpenBSD's ksh (mksh) is very poor, only making things worse, without really fixing it:

New bug, different from all the other shells:

mksh -ec 'eval "false; echo yup"'
yup

bash -ec 'eval "false; echo yup"'
(nothing)

Still not really fixed:

mksh -ec 'eval "set -e; false" || echo yup'
(nothing)

bash -ec 'eval "set -e; false" || echo yup'
yup

You can replace bash above with dash, zsh, yash, ksh93, etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.