This is a slightly esoteric Zsh question, of which there may not be a concrete answer.

I am trying to detect a particular context of a Zsh when instantiated in a particular way. How to do this is not immediately obvious given the environment variables commonly used for this purpose (such as $SHLVL, $ZSH_SUBSHELL, $ZSH_EVAL_CONTEXT, and $-), at least as far as I am aware.

Here is a Zsh instantiated in a normally detectable way:

export HELLO='world'
# 2 0 cmdarg 569X world

As opposed to a Zsh instantiated in a way that obscures detection:

export HELLO='world'
# 1 0 cmdarg 569X world

The Zsh called within the subshell has access to the parent environment's variables, as is evident by the presence of world, however $SHLVL is tricked into reporting 1 and unfortunately $ZSH_SUBSHELL, with a value of 0, is of no help in this case.

How can I detect, using standard detection methods, the fact that the second case has access to a parent environment? I don't want to have to rely on checking variables set by my self in the parent context that dirty the environment solely for this purpose, if I don't have to.

Perhaps it would help me if someone could explain how Zsh deciphers the $SHLVL and why placing that code in a subshell obscures that detection. My rough understanding is that the subshell forks the environment (anything that is exported), and that $SHLVL is not exported.

  • 1
    $SHLVL is exported. That's how it works, a shell adds one to the one it gets from the environment on start-up. Why it's decreased in subshells in zsh (printenv SHLVL vs (printenv SHLVL)) I'm not sure. Looks like a bug. – Stéphane Chazelas Aug 22 '16 at 6:05
  • I've raised the issue to the zsh development mailing list. Looks like zsh.org/mla/workers/1999/msg02472.html could be the culprit. The problem doesn't show up with zsh 3.1.5 – Stéphane Chazelas Aug 22 '16 at 6:21
  • Thanks for the sanity check; it made me to think to try from bash, and I've now noticed that (zsh) in bash has the expected $SHLVL of 2. So, the inconsistent/unexpected behavior is probably a bug! – leoj Aug 22 '16 at 7:13
  • @StéphaneChazelas Can you link to the mailing list issue that you raised? – leoj Aug 27 '16 at 19:34
  • 1
    zsh.org/mla/workers/2016/msg01574.html no answer yet – Stéphane Chazelas Aug 28 '16 at 8:14

That was a bug (regression) introduced in 1999 by that change and now fixed (by that change (commit))

Basically, in:

(cmd1; cmd2)

zsh forks a child process for the subshell, then another process for cmd1. But for cmd2, given that it's the last command in the subshell, zsh optimises out the fork. That's as if we had done:

(cmd1; exec cmd2)

Or in other words, zsh does an implicit/fake exec here.

Now the bug was that in that case, zsh forgot that we were in a subshell and assumed that implicit exec was actually terminating the shell (replacing it with cmd2), and was then decrementing $SHLVL as cmd2's parent would not be zsh anymore.

Here however, we're in a subshell, so that implicit exec is not terminating the shell.

Thanks for bringing that up. That allowed to fix it and find related bugs in bash and tcsh.

  • Thanks for pursuing this. I love the fact that it goes so far back in history regardless of the fact that it's an issue. – leoj Sep 5 '16 at 7:41

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.