Почему я не могу определить переменную только для чтения с именем path в zsh?

Почему я не могу определить переменную только для чтения с именем path в zsh?

В zsh path— это специальная переменная-массив, содержимое которой связано с известной PATHпеременной.

Настолько особенная, что определение и вызов функции

f() { local -r path=42 }

вызывает ошибку f: read-only variable: path. Если локальная переменная объявлена ​​как изменяемая (т.е. без -r), все работает как и ожидалось. Мне не удалось воспроизвести эту ошибку с другими именами переменных.

Почему возникает эта ошибка и является ли она преднамеренной? Существуют ли подобные правила для других имен?

Я использую zsh 5.2 (x86_64-apple-darwin16.0) на macOS 10.12.6.

решение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_READONLYset, в то время как специальные переменные, которые не справляются, имеют. Следующее очевидное место для поиска — это код, 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?), в противном случае пока не связывайтесь со специальными переменными.

Связанный контент