En zsh, path
es una variable de matriz especial, cuyo contenido está vinculado a la variable conocida PATH
.
De hecho, es tan especial que definir y llamar a la función
f() { local -r path=42 }
causa el error f: read-only variable: path
. Si la variable local se declara como mutable (es decir, sin -r
), todo funciona como se esperaba. No he podido reproducir este error con otros nombres de variables.
¿Por qué ocurre este error y es intencional? ¿Existen reglas similares para otros nombres?
Estoy usando zsh 5.2 (x86_64-apple-darwin16.0) en macOS 10.12.6.
Respuesta1
TL;DR no reutilice "parámetros incorporados especiales", como path
porque son especiales. O segúnLa lista de correose puede usar la -h
bandera:
% () { local -hr path=42; echo $path }
42
%
(Sin embargo, cambiar path
a un número entero podría estropear el código posterior que olvida esta anulación y asume que path
en su lugar es path
...)
Sigue una investigación más larga (pero me perdí por completo el -h
asunto de esconderse...)
% print ${(t)path}
array-special
Esta es una propiedad (¿característica? ¿Error?) de variables especiales, pero no de variables similares vinculadas por el usuario:
% () { 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
Hay varios otros parámetros que exhiben este comportamiento:
% 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
Entonces algunas variables son como enGranja de animalesmás especial que otros. Este mensaje de error proviene de varios lugares en Src/params.c
los que, si se modifica para imprimir, cuál es el mensaje específico que encontramos al compilar zsh
:
% () { local -r path }
% () { local -r path=foo }
(anon): read-only variable (setarrvalue): path
¿Es el código bastante genérico?
/**/
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;
}
Esto demuestra que el problema ocurre en otra parte; Las variables no especiales sin duda no se han PM_READONLY
establecido, mientras que las variables especiales que fallan sí lo hacen. El siguiente lugar obvio para buscar es el código local
que tiene varios nombres ( typeset
export
...). Todos estos son elementos incorporados, por lo que se pueden encontrar acechando en las profundidades deSrc/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),
Todos estos llaman bin_typeset
con varios indicadores configurados, así que estudiemos la fuente de esa función... juramento en los comentarios, verifique. Observa que la cosa está complicada, comprueba. En realidad, nada salta a la vista, aunque la madriguera del conejo (para cuando la -m
opción "tratar los argumentos como patrones" está activada ).noset, que es el caso aquí) parece conducir a la typeset_single
función...
Hay algo de código POSIXBUILTINS
relacionado con readonly
, pero está desactivado en mis shells de prueba.
% print $options[POSIXBUILTINS]
off
así que voy a ignorar ese código (espero. ¿Podría ser esto una guarida shoggoth y no una simple madriguera de conejo?). ¡Mientras tanto! Algunos puntos de depuración indican que la siguiente línea PM_READONLY
activa la banderapath
/*
* 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;
Lo cual a su vez proviene de la on
variable que a su vez ya está activada cuando typeset_single
se ingresa la función, suspiro, así que volvamos a bin_typeset
eso... bueno, básicamente hay una TYPESET_OPTSTR
que de alguna manera a través de algunas macros se habilita PM_READONLY
de forma predeterminada; cuando, en cambio, una variable proporcionada por el usuario se ejecuta a través de esta ruta de código, PM_READONLY
se desactiva y todo está bien.
Si esto se puede cambiar para que variables especiales como las que path
se pueden convertir en de solo lectura es una pregunta para un desarrollador de ZSH (¿pruebe la lista de correo de zsh-workers?); de lo contrario, mientras tanto, no pierda el tiempo con las variables especiales.