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
コードです( ...)。これらはすべて組み込み関数なので、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
オプションがオンになっている場合)ないここでのケースのように、セットは関数につながるようです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_OPTSTR
PM_READONLY
PM_READONLY
これを変更して、 などの特殊変数をpath
読み取り専用にできるかどうかは、ZSH 開発者への質問です (zsh-workers メーリング リストを試してください)。それ以外の場合は、その間、特殊変数をいじらないでください。