Ambito delle variabili locali nelle funzioni della shell

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.

Eseguilo online

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

  • È il comportamento previsto
  • Sono io lunico che pensa che ' sia strano che nellultima riga di output il valore di var sia vuoto? var è impostato a livello globale in innerFunc, quindi perché ' non si mantiene fino alla fine dello script?

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:

  1. 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 ksh function function_name { … }, la sua variabile locale obbedisce allo scoping lessicale / statico, quindi non sono visibili in altre funzioni chiamate da questo.

  2. il zsh/private plug-in caricabile automaticamente in zsh fornisce un private 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 o declare o local, 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 chiamato innerFunc tramite outFunc, non non stampare new 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] 

Sorgente

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *