Portée des variables locales dans les fonctions du shell

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.

Exécuter en ligne

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

  • Cest le comportement attendu
  • Suis-je le seul qui pense que ' est bizarre que sur la dernière ligne de sortie la valeur de var soit vide? var est défini globalement dans innerFunc, alors pourquoi ' reste-t-il jusquà la fin du script?

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:

  1. 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 ksh function 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.

  2. le zsh/private plug-in autoloadable dans zsh fournit un private 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 ou declare ou local, 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 via outFunc, ne ne pas imprimer new 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] 

Source

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *