Après avoir lu 24.2. Variables locales , je pensais que déclarer une variable var
avec le mot clé local
signifiait que var
« nétait accessible que dans le bloc de code délimité par les accolades dune fonction.
Cependant, après avoir exécuté lexemple suivant, jai découvert que var
est également accessible, lu et écrit à partir des fonctions invoquées par ce bloc de code – cest-à-dire même si var
est déclaré local
à outerFunc
, innerFunc
est toujours en mesure de le lire et de modifier sa valeur.
#!/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}]"
Résultat:
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 : Est-ce un bogue dans mon shell (bash 4.3.42, Ubuntu 16.04, 64bit) ou est-ce le comportement attendu?
MODIFIER: Résolu. Comme indiqué par @MarkPlotnick, cest bien le comportement attendu.
Commentaires
Réponse
Les variables shell ont un portée dynamique . Si une variable est déclarée comme locale à une fonction, cette portée reste jusquau retour de la fonction, y compris lors des appels à dautres fonctions.
Il y a deux exceptions:
-
dans ksh93, si une fonction est définie avec la syntaxe standard
function_name () { … }
, alors ses variables locales obéissent à la portée dynamique. Mais si une fonction est définie avec la syntaxe kshfunction function_name { … }
alors sa variable locale obéit à la portée lexicale / statique, donc elles ne sont pas visibles dans les autres fonctions appelées par cela. -
le
zsh/private
plug-in autoloadable danszsh
fournit unprivate
mot-clé / intégré qui peut être utilisé pour déclarer une variable avec une portée statique.
ash, bash, pdksh et dérivés, bosh nont quune portée dynamique.
Commentaires
- Toutes les variables du shell ont-elles une portée dynamique ou cela ne sapplique-t-il quaux variables déclarées avec
local
? - @HaroldFischer Toutes les variables ont une portée dynamique. Avec une déclaration
typeset
oudeclare
oulocal
, la portée est jusquau retour de la fonction. Sans une telle déclaration, la portée est globale. - À mon humble avis,
If a variable is declared as local to a function, that scope remains until the function returns.
nest pas suffisant pour expliquer ce quest la portée dynamique verus la portée lexicale. La description seule est également appliquée à la portée lexicale. - @jinbeomhong Non, avec la portée lexicale, une variable dune fonction nest pas visible alors que cette fonction appelle une autre fonction. Jai ' ajouté une phrase pour indiquer cela explicitement.
- Cela affecte-t-il également les fonctions internes dappel? Ou est-ce que les builtins ont leur propre portée?
Answer
Ce nest pas un bogue, lappel dans le contexte de externalFunc utilise cette copie locale de $ var. Le « local » dans externalFunc signifie que le global nest pas changé. Si vous appelez innerFunc en dehors de externalFunc, alors il y aura une modification dans le global $ var, mais pas dans le $ var local de OutsideFunc. Si vous avez ajouté « local » à innerFunc, alors outerFunc « s $ var » ne sera pas changé. – en substance, il « y en aurait 3:
- $ global :: var
- $ externalFunc :: var
- $ innerFunc :: var
pour utiliser le format despace de nom de Perl, en quelque sorte.
Réponse
Dans function innerFunc()
, le var="new value"
na pas été déclaré comme local , il est donc disponible dans une portée visible (une fois la fonction a été appelée).
Inversement, dans function outerFunc()
le local var="initial value"
a été déclaré comme local , donc il « nest pas disponible dans la portée globale (même si la fonction a été appelée).
Parce que innerFunc()
a été appelé en tant quenfant de outerFunc()
, var est dans la portée locale de outerFunc()
.
man 1 bash
peut aider à clarifier
local [option] [nom [= valeur] …]
Pour chaque argument, un local la variable nommée nom est créée et une valeur attribuée. Loption peut être lune des options acceptées par declare.Lorsque local est utilisé dans une fonction, le nom de la variable a une portée visible limitée à cette fonction et à ses enfants. …
Le comportement implicite attendu dans la description peut être obtenu en déclarant local var="new value
dans function innerFunc()
.
Comme dautres lont indiqué, ce nest pas un bogue dans le shell bash. Tout fonctionne comme il se doit.
Commentaires
- Votre première déclaration contredit ce que voit lutilisateur. Limpression de la valeur de
var
dans la portée globale, après avoir appeléinnerFunc
viaoutFunc
, ne ne pas imprimernew value
.
Réponse
Vous pouvez utiliser un fonction pour forcer la portée locale:
sh_local() { eval "$(set)" command eval "\"\$@\"" }
Exemple:
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"
Résultat:
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
soit vide?var
est défini globalement dansinnerFunc
, alors pourquoi ' reste-t-il jusquà la fin du script?