Zsh: Command Substitution Not Respecting err_exit

command-substitutionerror handlingzsh

I have a weird situation where inserting any command that succeeds in the line above makes command substitution ($(I mean this)) not work as expected — it does not exit, even though the command fails and err_exit is set.

I keep thinking it must be something to do with subshells behaving differently from what I think they should, but I can't quite put my finger on it.

This prints “should have exited above”:

#!/usr/bin/env zsh
setopt local_options err_exit warn_create_global nounset

local x
if false; then
else
    x=$(false)
    echo 'should have exited above'
fi

This prints nothing:

#!/usr/bin/env zsh
setopt local_options err_exit warn_create_global nounset

local x
if false; then
else
    true
    x=$(false)
    echo 'should have exited above'
fi

Obviously I can make my script work correctly by leaving that true in, but it doesn't seem correct and I can't even explain it. So what is happening, and what is the recommended solution?

Best Answer

It's a bug. Here's a smaller test case.

set -e; if false; then :; else x=$(false); echo "$? $-"; fi

The assignment x=$(false) should have the status of the last command substitution in it, which is 1. Without set -e, this snippet should print 1 followed by the active shell options. With set -e, this snippet should exit before it prints anything.

Zsh 5.1.1 works correctly (exit with status 1). Dash, bash, mksh, ksh93 all have the same behavior.

Zsh checked out from Git today prints 1 569Xe and exits with status 0. If you add a call to false before echo, the script exits as expected. It looks like errexit has no effect on x=$(false) even though it should. git bisect says b581c3fece76c87ed86ae9fc704d0fcf208a79d3 is the first bad commit.

It's now been reported on the zsh workers mailing list and fixed a few hours later in that commit which will be included in the next release.

Related Question