Bereik van lokale variabelen in shell-functies

Na het lezen van 24.2. Lokale variabelen , dacht ik dat het declareren van een variabele var met het trefwoord local betekende dat var “s waarde was alleen toegankelijk binnen het codeblok dat wordt afgebakend door de accolades van een functie.

Na het uitvoeren van het volgende voorbeeld kwam ik er echter achter dat var kan ook worden geopend, gelezen en geschreven vanuit de functies die door dat codeblok worden aangeroepen – dwz zelfs als var is gedeclareerd local naar outerFunc, innerFunc kan het nog steeds lezen en de waarde ervan wijzigen.

Voer het online uit

#!/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}]" 

Uitvoer:

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 : Is dat een bug in mijn shell (bash 4.3.42, Ubuntu 16.04, 64bit) of is het het verwachte gedrag?

EDIT: Opgelost. Zoals opgemerkt door @MarkPlotnick, is dit inderdaad het verwachte gedrag.

Opmerkingen

  • Het is het verwachte gedrag
  • Ben ik de enige die denkt dat het ‘ raar is dat op de laatste regel van de uitvoer de waarde van var leeg is? var is globaal ingesteld in innerFunc, dus waarom blijft ‘ niet tot het einde van het script?

Answer

Shell-variabelen hebben een dynamisch bereik . Als een variabele wordt gedeclareerd als lokaal voor een functie, blijft die scope bestaan totdat de functie terugkeert, ook tijdens het aanroepen van andere functies.

Er zijn twee uitzonderingen:

  1. in ksh93, als een functie is gedefinieerd met de standaard function_name () { … } -syntaxis, gehoorzamen de lokale variabelen aan dynamische scoping. Maar als een functie is gedefinieerd met de ksh-syntaxis function function_name { … }, dan gehoorzamen de lokale variabele lexicale / statische scoping, zodat ze niet zichtbaar zijn in andere functies die hierdoor worden aangeroepen.

  2. de zsh/private plug-in voor automatisch laden in zsh biedt een private trefwoord / ingebouwd dat kan worden gebruikt om een variabele met statische reikwijdte te declareren.

ash, bash, pdksh en afgeleiden, bosh hebben alleen dynamische scoping.

Opmerkingen

  • Hebben alle variabelen in de shell een dynamisch bereik of is dit alleen van toepassing op variabelen die zijn gedeclareerd met local?
  • @HaroldFischer Alle variabelen hebben een dynamisch bereik. Met een typeset of declare of local declaratie, is het bereik totdat de functie terugkeert. Zonder zon verklaring is de scope globaal.
  • IMHO, If a variable is declared as local to a function, that scope remains until the function returns. is niet genoeg om uit te leggen wat dynamische scope versus lexicale scope is. De beschrijving alleen wordt ook toegepast op lexicale scope.
  • @jinbeomhong Nee, met lexicale scope is een variabele van een functie niet zichtbaar terwijl deze functie een andere functie aanroept. Ik ‘ heb een zin toegevoegd om dit expliciet te vermelden.
  • Heeft dit ook invloed op het aanroepen van ingebouwde functies? Of hebben ingebouwde versies hun eigen bereik?

Antwoord

Het is geen “bug”, de oproep binnen de context van de outerFunc gebruikt die lokale kopie van $ var. De “local” in outerFunc betekent dat de globale niet is gewijzigd. Als je innerFunc buiten outerFunc aanroept, dan zal er een wijziging zijn in de global $ var, maar niet de outerFunc s lokale $ var. Als je local aan innerFunc hebt toegevoegd, dan zal outerFunc s $ var niet worden gewijzigd – in wezen “zouden er 3 zijn:

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

om de naamruimte-indeling van Perl te gebruiken, soort van.

Antwoord

In function innerFunc() is de var="new value" niet “gedeclareerd als lokaal , daarom is het beschikbaar in een zichtbaar bereik (eenmaal de functie is aangeroepen).

Omgekeerd werd in function outerFunc() de local var="initial value" gedeclareerd als local , daarom is het niet beschikbaar in de globale scope (zelfs als de functie is aangeroepen).

Omdat innerFunc() werd aangeroepen als een kind van outerFunc(), var valt binnen het lokale bereik van outerFunc().

man 1 bash kan helpen verduidelijken

local [optie] [naam [= waarde] …]

Voor elk argument, een lokale variabele met de naam naam wordt gemaakt, en een toegewezen waarde. De optie kan een van de opties zijn die door aangifte worden geaccepteerd.Wanneer lokaal binnen een functie wordt gebruikt, zorgt dit ervoor dat de variabelenaam een zichtbaar bereik heeft dat beperkt is tot die functie en zijn onderliggende items. …

Het geïmpliceerde gedrag dat wordt verwacht in de beschrijving kan worden bereikt door local var="new value in function innerFunc().

Zoals anderen hebben gezegd, is dit geen bug in de bash-shell. Alles functioneert zoals het hoort.

Opmerkingen

  • Uw eerste verklaring is in tegenspraak met wat de gebruiker ziet. Het afdrukken van de waarde van var in het globale bereik, na het aanroepen van innerFunc tot en met outFunc, doet niet afdrukken new value.

Antwoord

U kunt een functie om lokaal bereik te forceren:

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

Voorbeeld:

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" 

Resultaat:

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] 

Bron

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *