Dopo aver letto 24.2. Variabili locali , pensavo che dichiarare una variabile var
con la parola chiave local
significasse che var
“il valore era accessibile solo allinterno del blocco di codice delimitato dalle parentesi graffe di una funzione.
Tuttavia, dopo aver eseguito il seguente esempio, ho scoperto che var
è anche possibile accedere, leggere e scrivere dalle funzioni invocate da quel blocco di codice, cioè anche se var
è dichiarato local
a outerFunc
, innerFunc
è ancora in grado di leggerlo e modificarne il valore.
#!/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}]"
Risultato:
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 : È un bug nella mia shell (bash 4.3.42, Ubuntu 16.04, 64bit) o è il comportamento previsto?
MODIFICA: risolto. Come notato da @MarkPlotnick, questo è effettivamente il comportamento previsto.
Commenti
Risposta
Le variabili di shell hanno un ambito dinamico . Se una variabile viene dichiarata come locale a una funzione, tale ambito rimane fino a quando la funzione non ritorna, anche durante le chiamate ad altre funzioni.
Ci sono due eccezioni:
-
in ksh93, se una funzione è definita con la sintassi
function_name () { … }
standard, le sue variabili locali obbediscono allambito dinamico. Ma se una funzione è definita con la sintassi kshfunction function_name { … }
, la sua variabile locale obbedisce allo scoping lessicale / statico, quindi non sono visibili in altre funzioni chiamate da questo. -
il
zsh/private
plug-in caricabile automaticamente inzsh
fornisce unprivate
parola chiave / incorporata che può essere utilizzata per dichiarare una variabile con ambito statico.
ash, bash, pdksh e derivati, bosh hanno solo uno scoping dinamico.
Commenti
- Tutte le variabili nella shell hanno un ambito dinamico o questo si applica solo alle variabili dichiarate con
local
? - @HaroldFischer Tutte le variabili hanno ambito dinamico. Con una dichiarazione
typeset
odeclare
olocal
, lambito è finché la funzione non viene restituita. Senza tale dichiarazione, lambito è globale. - IMHO,
If a variable is declared as local to a function, that scope remains until the function returns.
non è sufficiente per spiegare cosè lambito dinamico rispetto allambito lessicale. La sola descrizione viene applicata anche allambito lessicale. - @jinbeomhong No, con lambito lessicale, una variabile di una funzione non è visibile mentre questa funzione chiama unaltra funzione. ' ho aggiunto una frase per dichiararlo esplicitamente.
- Questo influisce anche sui comandi incorporati di chiamata di funzioni? O i builtin hanno il loro ambito?
Answer
Non è “un bug, la chiamata allinterno del contesto di outerFunc utilizza la copia locale di $ var. “local” in outerFunc significa che la global non è cambiata. Se chiami innerFunc al di fuori di outerFunc, ci sarà una modifica alla $ var globale, ma non alla $ var locale di outerFunc “. Se hai aggiunto” local “a innerFunc, la $ var di outerFunc” non verrebbe modificata – in sostanza, ce ne sarebbero 3:
- $ global :: var
- $ outerFunc :: var
- $ innerFunc :: var
per usare il formato dello spazio dei nomi di Perl, più o meno.
Risposta
In function innerFunc()
var="new value"
non è stato dichiarato come locale , quindi è disponibile nellambito visibile (una volta la funzione è stata chiamata).
Al contrario, in function outerFunc()
il local var="initial value"
è stato dichiarato locale , quindi non è disponibile nellambito globale (anche se la funzione è stata chiamata).
Poiché innerFunc()
è stato chiamato come figlio di outerFunc()
, var rientra nellambito locale di outerFunc()
.
man 1 bash
può aiutare a chiarire
local [opzione] [nome [= valore] …]
Per ogni argomento, un locale viene creata la variabile denominata nome e viene assegnato il valore. Lopzione può essere una qualsiasi delle opzioni accettate da declare.Quando local viene utilizzato allinterno di una funzione, fa sì che il nome della variabile abbia un ambito visibile limitato a quella funzione e ai suoi figli. …
Il comportamento implicito che “è previsto nella descrizione può essere ottenuto dichiarando local var="new value
in function innerFunc()
.
Come altri hanno affermato, questo non è un bug nella shell bash. Tutto funziona come dovrebbe.
Commenti
- La tua prima affermazione contraddice ciò che lutente sta vedendo. Stampando il valore di
var
nellambito globale, dopo aver chiamatoinnerFunc
tramiteoutFunc
, non non stamparenew value
.
Risposta
Puoi utilizzare un funzione per forzare lambito locale:
sh_local() { eval "$(set)" command eval "\"\$@\"" }
Esempio:
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"
Risultato:
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
sia vuoto?var
è impostato a livello globale ininnerFunc
, quindi perché ' non si mantiene fino alla fine dello script?