Alcance de las variables locales en las funciones del shell

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.

Ejecutarlo en línea

#!/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

  • Es el comportamiento esperado
  • ¿Estoy ¿El único que piensa que es ' extraño que en la última línea de salida el valor de var esté vacío? var se establece globalmente en innerFunc, entonces, ¿por qué no ' no se mantiene hasta el final? del script?

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:

  1. 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 ksh function 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.

  2. el zsh/private complemento autocargable en zsh proporciona un private 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 o declare o local, 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 a innerFunc a través de outFunc, no no imprima new 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] 

Fuente

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *