这是一个有点深奥的 Zsh 问题,可能没有具体的答案。
我试图检测 Zsh 以特定方式实例化时的特定上下文。考虑到通常用于此目的的环境变量(例如$SHLVL
、$ZSH_SUBSHELL
、$ZSH_EVAL_CONTEXT
和$-
),至少据我所知,如何做到这一点并不是立即显而易见的。
这是一个以通常可检测的方式实例化的 Zsh:
export HELLO='world'
zsh -c 'echo "$SHLVL $ZSH_SUBSHELL $ZSH_EVAL_CONTEXT $- $HELLO"'
# 2 0 cmdarg 569X world
与以掩盖检测的方式实例化的 Zsh 相反:
export HELLO='world'
(zsh -c 'echo "$SHLVL $ZSH_SUBSHELL $ZSH_EVAL_CONTEXT $- $HELLO"')
# 1 0 cmdarg 569X world
在子 shell 中调用的 Zsh 可以访问父环境的变量,正如 的存在所表明的那样world
,但是$SHLVL
被欺骗进行报告1
,不幸的是$ZSH_SUBSHELL
,其值为0
,在这种情况下没有任何帮助。
如何使用标准检测方法检测第二个案例可以访问父环境的事实?如果不需要的话,我不想仅仅为了这个目的而依赖于检查我自己在父上下文中设置的变量,这些变量会污染环境。
$SHLVL
如果有人可以解释 Zsh 如何破译以及为什么将该代码放在子 shell 中会掩盖该检测,也许会对我有所帮助。我的粗略理解是,子 shell 分叉了环境(导出的任何内容),并且$SHLVL
不导出。
答案1
那是一个漏洞(回归)于 1999 年引入那个改变现在已修复(通过那个改变(犯罪))
基本上,在:
(cmd1; cmd2)
zsh
为 subshell 分叉一个子进程,然后为cmd1
.但是对于cmd2
,考虑到它是子 shell 中的最后一个命令,zsh
优化了 fork。就好像我们已经做了:
(cmd1; exec cmd2)
或者换句话说zsh
,隐含的/伪造的 exec
这里。
现在的错误是在那种情况下,zsh
忘记我们处于子 shell 中并假设隐含的 exec
实际上是终止shell(用 替换它cmd2
),然后递减,$SHLVL
因为cmd2
的父级将不再存在zsh
。
然而,在这里,我们处于子外壳中,因此隐含的exec
没有终止外壳。