Después de leer 24.2. Variables locales , pensé que declarar una variable var
con la palabra clave local
significaba que var
solo era accesible dentro del bloque de código delimitado por las llaves de una función.
Sin embargo, después de ejecutar el siguiente ejemplo, descubrí que var
también se puede acceder, leer y escribir desde las funciones invocadas por ese bloque de código, es decir, aunque var
esté declarado local
a outerFunc
, innerFunc
aún puede leerlo y modificar su valor.
#!/usr/bin/env bash function innerFunc() { var="new value" echo "innerFunc: [var:${var}]" } function outerFunc() { local var="initial value" echo "outerFunc: before innerFunc: [var:${var}]" innerFunc echo "outerFunc: after innerFunc: [var:${var}]" } echo "global: before outerFunc: [var:${var}]" outerFunc echo "global: after outerFunc: [var:${var}]"
Resultado:
global: before outerFunc: [var:] # as expected, `var` is not accessible outside of `outerFunc` outerFunc: before innerFunc: [var:initial value] innerFunc: [var:new value] # `innerFunc` has access to `var` ?? outerFunc: after innerFunc: [var:new value] # the modification of `var` by `innerFunc` is visible to `outerFunc` ?? global: after outerFunc: [var:]
Q : ¿Es un error en mi shell (bash 4.3.42, Ubuntu 16.04, 64bit) o es el comportamiento esperado?
EDITAR: Resuelto. Como señaló @MarkPlotnick, este es de hecho el comportamiento esperado.
Comentarios
Respuesta
Las variables de shell tienen un alcance dinámico . Si una variable se declara como local a una función, ese alcance permanece hasta que la función regresa, incluso durante las llamadas a otras funciones.
Hay dos excepciones:
-
en ksh93, si una función se define con la sintaxis
function_name () { … }
estándar, entonces sus variables locales obedecen al alcance dinámico. Pero si una función se define con la sintaxis kshfunction function_name { … }
, entonces su variable local obedece al ámbito léxico / estático, por lo que no son visibles en otras funciones llamadas por este. -
el
zsh/private
complemento autocargable enzsh
proporciona unprivate
palabra clave / incorporado que se puede usar para declarar una variable con alcance estático.
ash, bash, pdksh y derivados, bosh solo tienen alcance dinámico.
Comentarios
- ¿Todas las variables en el shell tienen un alcance dinámico o esto solo se aplica a las variables declaradas con
local
? - @HaroldFischer Todas las variables tienen alcance dinámico. Con una declaración
typeset
odeclare
olocal
, el alcance es hasta que la función regrese. Sin tal declaración, el alcance es global. - En mi humilde opinión,
If a variable is declared as local to a function, that scope remains until the function returns.
no es suficiente para explicar qué es alcance dinámico versus alcance léxico. La descripción sola también se aplica al alcance léxico. - @jinbeomhong No, con alcance léxico, una variable de una función no es visible mientras esta función llama a otra función. ' he agregado una oración para indicar esto explícitamente.
- ¿Esto también afecta a las funciones incorporadas que llaman? ¿O las incorporaciones tienen su propio alcance?
Respuesta
No es un error, la llamada dentro del contexto de la función externa utiliza esa copia local de $ var. El «local» en la función externa significa que el global no ha cambiado. Si llama a innerFunc fuera de outerFunc, habrá un cambio en el $ var global, pero no en el $ var local de outerFunc. Si agregó «local» a innerFunc, el $ var de outerFunc «no se cambiaría – en esencia, habría 3 de ellos:
- $ global :: var
- $ externalFunc :: var
- $ innerFunc :: var
para usar el formato de espacio de nombres de Perl, más o menos.
Respuesta
En function innerFunc()
el var="new value"
no se declaró como local , por lo tanto, está disponible en el alcance visible (una vez la función ha sido llamada).
Por el contrario, en function outerFunc()
la local var="initial value"
se declaró como local , por lo tanto, no está disponible en el ámbito global (incluso si se ha llamado a la función).
Porque innerFunc()
fue llamado como hijo de outerFunc()
, var está dentro del alcance local de outerFunc()
.
man 1 bash
puede ayudar a aclarar
local [opción] [nombre [= valor] …]
Para cada argumento, un Se crea la variable denominada nombre y se le asigna un valor. La opción puede ser cualquiera de las opciones aceptadas por declare.Cuando se usa local dentro de una función, hace que el nombre de la variable tenga un alcance visible restringido a esa función y sus hijos. …
El comportamiento implícito que se espera en la descripción se puede lograr declarando local var="new value
en function innerFunc()
.
Como han dicho otros, esto no es un error en el shell bash. Todo está funcionando como debería.
Comentarios
- Su primera declaración contradice lo que ve el usuario. Imprimir el valor de
var
en el ámbito global, después de llamar ainnerFunc
a través deoutFunc
, no no imprimanew value
.
Respuesta
Puede utilizar un función para forzar el alcance local:
sh_local() { eval "$(set)" command eval "\"\$@\"" }
Ejemplo:
x() { z="new value" printf "function x, z = [%s]\n" "$z" } y() { z="initial value" printf "function y before x, z = [%s]\n" "$z" sh_local x printf "function y after x, z = [%s]\n" "$z" } printf "global before y, z = [%s]\n" "$z" y printf "global after y, z = [%s]\n" "$z"
Resultado:
global before y, z = [] function y before x, z = [initial value] function x, z = [new value] function y after x, z = [initial value] global after y, z = [initial value]
var
esté vacío?var
se establece globalmente eninnerFunc
, entonces, ¿por qué no ' no se mantiene hasta el final? del script?