為什麼我不能在 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 郵件列表?),否則同時不要亂搞特殊變數。

相關內容