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