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整数に変更すると、このオーバーライドを忘れてpath代わりにであると想定する後続のコードが混乱する可能性がありますpath...)

さらに長い掘り出し物が続きます (しかし、私は-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コードです( ...)。これらはすべて組み込み関数なので、localtypeset exportSrc/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オプションがオンになっている場合)ないここでのケースのように、セットは関数につながるようですtypeset_single...

POSIXBUILTINSに関連するコードがいくつかありますreadonlyが、テストシェルではオフになっています。

% 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_OPTSTRPM_READONLYPM_READONLY

これを変更して、 などの特殊変数をpath読み取り専用にできるかどうかは、ZSH 開発者への質問です (zsh-workers メーリング リストを試してください)。それ以外の場合は、その間、特殊変数をいじらないでください。

関連情報