为什么我不能在 zsh 中定义名为 path 的只读变量?

为什么我不能在 zsh 中定义名为 path 的只读变量?

在zsh中,path是一个特殊的数组变量,其内容链接到众所周知的PATH变量。

事实上,定义和调用该函数非常特别

f() { local -r path=42 }

导致错误f: read-only variable: path。如果局部变量被声明为可变的(即没有-r),一切都会按预期工作。我无法使用其他变量名称重现此错误。

为什么会出现此错误?这是故意的吗?其他名称是否也存在类似规则?

我在 macOS 10.12.6 上使用 zsh 5.2 (x86_64-apple-darwin16.0)。

答案1

TL;DR 不要重复使用“特殊内置参数”,例如path因为它们很特殊。或者根据邮件列表可以使用该-h标志:

% () { local -hr path=42; echo $path }
42
% 

(但是更改path为整数可能会弄乱后续代码,这些代码会忘记此覆盖并假设pathpath......)

接下来是更长时间的挖掘(但我完全错过了-h隐藏的事情......)

% print ${(t)path}
array-special

这是特殊变量的属性(功能?错误?),但不是用户链接的类似变量:

% () { typeset -r PATH=/blah; echo $PATH }
(anon): read-only variable: PATH
% typeset -Tar FOO=bar foo
% print $foo
bar
% print ${(t)foo}
array-readonly-tag_local
% () { local -r foo=blah; echo $foo }
blah

还有各种其他参数表现出这种行为:

% for p in $parameters[(I)*]; do print $p $parameters[$p]; done | grep array-
cdpath array-special
...
% () { local -r cdpath=42 }
(anon): read-only variable: cdpath

所以有些变量就像动物农场比其他人更特别。此错误消息来自各个地方,其中Src/params.c如果将其修改为打印哪条消息,则我们在编译时会发现该消息是特定的zsh

% () { local -r path }
% () { local -r path=foo }
(anon): read-only variable (setarrvalue): path

是相当通用的代码

/**/
mod_export void
setarrvalue(Value v, char **val)
{
    if (unset(EXECOPT))
        return;
    if (v->pm->node.flags & PM_READONLY) {
        zerr("read-only variable (setarrvalue): %s", v->pm->node.nam);
        freearray(val);
        return;
    }

这表明问题发生在其他地方;非特殊变量无疑没有PM_READONLY设置,而失败的特殊变量却设置了。下一个值得注意的地方是local具有各种名称的代码( typeset export...)。这些都是内置的,所以可以发现潜伏在Src/builtin.c

% grep BUILTIN Src/builtin.c | grep local
    BUILTIN("local", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "AE:%F:%HL:%R:%TUZ:%ahi:%lp:%rtux", NULL),

这些都是bin_typeset设置了各种标志的调用,所以让我们研究该函数的源代码......在评论中发誓,检查。注意到事情很复杂,检查一下。尽管兔子洞(当“将参数视为模式”-m选项是不是set,这里就是这种情况)似乎导致了这个typeset_single功能......

有一些与POSIXBUILTINS相关的代码readonly,但在我的测试 shell 中已关闭

% print $options[POSIXBUILTINS]
off

所以我将忽略该代码(我希望如此。这可能是修格斯的巢穴而不仅仅是兔子洞吗?)。同时!一些调试指向通过以下行PM_READONLY打开的标志path

    /*
     * The remaining on/off flags should be harmless to use,
     * because we've checked for unpleasant surprises above.
     */
    pm->node.flags = (PM_TYPE(pm->node.flags) | on | PM_SPECIAL) & ~off;

这又来自于输入on函数时已经打开的变量typeset_single,叹息,所以回到bin_typeset我们开始...好吧,基本上有一个TYPESET_OPTSTR通过某些宏以某种方式默认启用的PM_READONLY;相反,当用户提供的变量通过此代码路径运行时,该变量PM_READONLY将被关闭并且一切正常。

对于 ZSH 开发人员来说,是否可以更改此设置以使特殊变量(例如path可以只读)是一个问题(尝试 zsh-workers 邮件列表?),否则同时不要乱搞特殊变量。

相关内容