Gültigkeitsbereich lokaler Variablen in Shell-Funktionen

28

Nach dem Lesen von 24.2. Lokale Variablen Ich dachte, dass das Deklarieren einer Variablen varmit dem Schlüsselwort localbedeutet, dass auf varden Wert nur innerhalb des Codeblocks zugegriffen werden kann, der durch die geschweiften Klammern einer Funktion begrenzt wird.

Doch nach dem folgende Beispiel ausgeführt wird , fand ich , dass aus varauch zugegriffen werden kann, lesen und von den Funktionen , die durch diesen Block von Code aufgerufen geschrieben - dh auch wenn varerklärt wird , localzu outerFunc, innerFuncist noch in der Lage , es zu lesen und seinen Wert zu verändern.

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

Ausgabe:

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

F: Ist das ein Fehler in meiner Shell (bash 4.3.42, Ubuntu 16.04, 64bit) oder ist es das erwartete Verhalten?

EDIT: Gelöst. Wie von @MarkPlotnick festgestellt, ist dies in der Tat das erwartete Verhalten.

Maddouri
quelle
Es ist das erwartete Verhalten
fpmurphy
2
Bin ich der einzige, der denkt, es sei komisch, dass in der letzten Ausgabezeile der Wert von varleer ist? varist global festgelegt. innerFuncWarum bleibt sie also nicht bis zum Ende des Skripts erhalten?
Harold Fischer

Antworten:

22

Shell-Variablen haben einen dynamischen Gültigkeitsbereich . Wenn eine Variable als lokal für eine Funktion deklariert wird, bleibt dieser Bereich bestehen, bis die Funktion zurückgegeben wird.

Es gibt zwei Ausnahmen:

  1. Wenn in ksh93 eine Funktion mit der Standardsyntax definiert function_name () { … }ist, gehorchen ihre lokalen Variablen dem dynamischen Gültigkeitsbereich. Wenn eine Funktion mit der ksh-Syntax definiert ist, unterliegt function function_name { … }ihre lokale Variable dem lexikalischen / statischen Gültigkeitsbereich, sodass sie in anderen von dieser Funktion aufgerufenen Funktionen nicht sichtbar sind.

  2. Das automatisch ladbare zsh/privatePlugin zshbietet ein privateSchlüsselwort / Builtin, mit dem eine Variable mit statischem Gültigkeitsbereich deklariert werden kann.

ash, bash, pdksh und derivate, bosh haben nur dynamisches scoping.

Gilles 'SO - hör auf böse zu sein'
quelle
Haben alle Variablen in der Shell einen dynamischen Gültigkeitsbereich oder gilt dies nur für Variablen, die mit deklariert wurden local?
Harold Fischer
@HaroldFischer Alle Variablen haben dynamischen Gültigkeitsbereich. Mit einer typesetoder declareoder localDeklaration bleibt der Gültigkeitsbereich solange bestehen, bis die Funktion zurückkehrt. Ohne eine solche Erklärung ist der Geltungsbereich global.
Gilles 'SO - hör auf böse zu sein'
6

Es ist kein Fehler, der Aufruf innerhalb des Kontexts der OuterFunc verwendet diese lokale Kopie von $ var. Das "lokale" in OuterFunc bedeutet, dass das globale nicht geändert wird. Wenn Sie innerFunc außerhalb von outerFunc aufrufen, wird die globale $ var geändert, nicht jedoch die lokale $ var der outerFunc. Wenn Sie innerFunc "local" hinzufügen, wird $ var von outerFunc nicht geändert - im Wesentlichen gibt es drei davon:

  • $ global :: var
  • $ outerFunc :: var
  • $ innerFunc :: var

Um das Namespace-Format von Perl zu verwenden, verwenden Sie eine Art.

afbach
quelle
2

Sie können eine Funktion verwenden, um den lokalen Bereich zu erzwingen:

sh_local() {
  eval "$(set)" command eval '\"\$@\"'
}

Beispiel:

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"

Ergebnis:

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]

Quelle

Steven Penny
quelle
2

In function innerFunc()dem var='new value'nicht wie erklärt wurde , lokal , deshalb ist es im sichtbaren Bereich zur Verfügung (wenn die Funktion aufgerufen wurde).

Im Gegensatz dazu in function outerFunc()dem local var='initial value'wie erklärt wurde , lokal , deshalb ist es nicht im globalen Bereich (auch wenn die Funktion aufgerufen wurde).

Da var innerFunc()als Kind von aufgerufen wurde outerFunc(), liegt es im lokalen Geltungsbereich von outerFunc().

man 1 bash kann helfen zu klären

local [Option] [Name [= Wert] ...]

Für jedes Argument wird eine lokale Variable namens name erstellt und ein Wert zugewiesen. Die Option kann eine der durch declare akzeptierten Optionen sein. Wenn local in einer Funktion verwendet wird, wird für den Variablennamen ein sichtbarer Bereich festgelegt, der auf diese Funktion und ihre untergeordneten Elemente beschränkt ist. ...

Das implizite Verhalten, das in der Beschreibung erwartet wird, kann durch Deklaration von local var='new valuein erreicht werden function innerFunc().

Wie andere gesagt haben, ist dies kein Fehler in der Bash-Shell. Alles funktioniert wie es sollte.

Joseph Tingiris
quelle
Ihre erste Aussage widerspricht dem, was der Benutzer sieht. Drucke den Wert varim globalen Bereich, nach dem Aufruf innerFuncdurch outFunc, druckt nicht new value.
Kusalananda