Ambito delle variabili locali nelle funzioni della shell


28

Dopo aver letto 24.2. Variabili locali , ho pensato che dichiarare una variabile varcon la parola chiave localsignificasse che varil valore era accessibile solo all'interno del blocco di codice delimitato dalle parentesi graffe di una funzione.

Tuttavia, dopo aver eseguito il seguente esempio, ho scoperto che varsi può accedere anche, letti e scritti dalle funzioni invocate da quel blocco di codice - vale a dire, anche se varè dichiarato locala outerFunc, innerFuncè ancora in grado di leggere e modificare il suo valore.

Run It 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}]"

Produzione:

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:]

D: È un bug nella mia shell (bash 4.3.42, Ubuntu 16.04, 64bit) o ​​è il comportamento previsto?

EDIT: risolto. Come notato da @MarkPlotnick, questo è davvero il comportamento previsto.


È il comportamento atteso
fpmurphy,

2
Sono l'unico a pensare che sia strano che sull'ultima riga dell'output il valore di varsia vuoto? varè impostato a livello globale innerFunc, quindi perché non si attacca fino alla fine della sceneggiatura?
Harold Fischer,

Risposte:


22

Le variabili della shell hanno un ambito dinamico . Se una variabile viene dichiarata come locale in una funzione, tale ambito rimane fino al ritorno della funzione.

Esistono due eccezioni:

  1. in ksh93, se una funzione è definita con la function_name () { … }sintassi standard , le sue variabili locali obbediscono all'ambito dinamico. Ma se una funzione è definita con la sintassi ksh, la function function_name { … }sua variabile locale obbedisce all'ambito lessicale / statico, quindi non sono visibili in altre funzioni chiamate da questo.

  2. il zsh/privateplugin autoloadable in zshfornisce una privateparola chiave / builtin che può essere usata per dichiarare una variabile con ambito statico.

ash, bash, pdksh e derivati, bosh hanno solo scoping dinamico.


Tutte le variabili nella shell hanno un ambito dinamico o questo si applica solo alle variabili dichiarate con local?
Harold Fischer il

@HaroldFischer Tutte le variabili hanno un ambito dinamico. Con una typeseto declareo localdichiarazione, l'ambito è fino a quando non viene restituita la funzione. Senza tale dichiarazione, il campo di applicazione è globale.
Gilles 'SO- smetti di essere malvagio' il

6

Non è un bug, la chiamata all'interno del contesto di outerFunc utilizza quella copia locale di $ var. Il "locale" in outerFunc significa che il globale non è cambiato. Se chiami innerFunc al di fuori di outerFunc, allora ci sarà una modifica al $ var globale, ma non al $ var locale di externalFunc. Se aggiungessi "local" a innerFunc, allora $ var di outerFunc non verrebbe modificato - in sostanza, ce ne sarebbero 3:

  • $ Globale :: var
  • $ OuterFunc :: var
  • $ InnerFunc :: var

per usare il formato dello spazio dei nomi di Perl, una specie di.


2

È possibile utilizzare una funzione per forzare l'ambito 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]

fonte


2

Nel function innerFunc()l' var='new value'non è stata dichiarata come locale , quindi è disponibile in ambito visibile (una volta che la funzione è stata chiamata).

Al contrario, in function outerFunc()la local var='initial value'è stato dichiarato come locale , quindi non è disponibile in ambito globale (anche se la funzione è stata chiamata).

Poiché è innerFunc()stato chiamato come figlio di outerFunc(), var rientra nell'ambito locale di outerFunc().

man 1 bash può aiutare a chiarire

local [opzione] [nome [= valore] ...]

Per ogni argomento, viene creata una variabile locale denominata name e viene assegnato un valore. L'opzione può essere una qualsiasi delle opzioni accettate da dichiarare. Quando local è usato all'interno 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 previsto nella descrizione potrebbe essere ottenuto dichiarando local var='new valuein function innerFunc().

Come altri hanno già detto, questo non è un bug nella shell bash. Tutto funziona come dovrebbe.


La tua prima affermazione contraddice ciò che l'utente vede. Stampa del valore varin ambito globale, dopo aver chiamato innerFuncattraverso outFunc, non stampa new value.
Kusalananda
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.