24.2. Variables locales

Que fait une variable locale ?

variables locales

Une variable déclarée localement n'est visible qu'à l'intérieur du bloc de code dans laquelle elle apparaît. Elle a une visibilité locale. Dans une fonction, une variable locale n'a de signification qu'à l'intérieur du bloc de la fonction. [106]

Exemple 24.12. Visibilité de la variable locale

#!/bin/bash
# Variables globales et locales à l'intérieur d'une fonction.

fonc ()
{
  local var_local=23       # Déclaré en tant que variable locale.
  echo                     # Utilise la commande intégrée locale.
  echo "\"var_local\" dans la fonction = $var_local"
  var_global=999           # Non déclarée en local.
                           # Retour en global.
  echo "\"var_global\" dans la fonction = $var_global"
}  

fonc

# Maintenant, voyons s'il existe une variable locale en dehors de la fonction.

echo
echo "\"var_loc\" en dehors de la fonction = $var_loc"
                                      # "var_loc" en dehors de la fonction = 
                                      # Non, $var_local n'est pas visible globalement.
echo "\"var_global\" en dehors de la fonction = $var_global"
                                      # "var_global" en dehors de la fontion = 999
                                      # $var_global est visible globalement.
echo                                  

exit 0
#  Au contraire de C, une variable Bash déclarée dans une fonction n'est locale
#+ que si elle est déclarée ainsi.


[Attention]

Attention

Avant qu'une fonction ne soit appelée, toutes les variables déclarées dans la fonction sont invisibles à l'extérieur du corps de la fonction, et pas seulement celles déclarées explicitement locales.

#!/bin/bash

fonc ()
{
var_globale=37   #  Visible seulement à l'intérieur du bloc de la fonction
                 #+ avant que la fonction ne soit appelée.
}                # FIN DE LA FONCTION

echo "var_globale = $var_globale" # var_globale =
                                  #  La fonction "fonc" n'a pas encore été appelée,
                                  #+ donc $var_globale n'est pas visible ici.

fonc
echo "var_globale = $var_globale" # var_globale = 37
                                  # A été initialisée par l'appel de la fonction.
[Note]

Note

Ainsi qu'Evgeniy Ivanov nous le fait remarquer, quand on déclare une variable locale et qu'on lui assigne une valeur au sein d'une même commande, il semble que l'ordre des opérations soit de premièrement assigner une valeur à la variable et seulement ensuite restreindre sa visibilité localement. Ce point est reflété par la valeur de retour.

#!/bin/bash

echo "=À L'EXTÉRIEUR DE Fonction (global)=="
t=$(exit 1)
echo $?      # 1
             # Comme prévu.
echo

fonction0 ()
{

echo "==À L'INTÉRIEUR DE Fonction=="
echo "Global"
t0=$(exit 1)
echo $?      # 1
             # Comme prévu.

echo
echo "Variable locale déclarée & assignée en une même commande."
local t1=$(exit 1)
echo $?      # 0
             # Imprévu !
#  Il semble que l'assignement d'une valeur à la variable se produise
#+ avant la déclaration locale.
#+ La valeur de retour est celle de cette dernière.

echo
echo "D'abord la déclaration locale, ensuite l'assignement (commandes séparées)."
local t2
t2=$(exit 1)
echo $?      # 1
             # Comme prévu.

}

fonction0

24.2.1. Variables locales et récursion

Les variables locales sont un outil intéressant pour écrire du code récursif, mais cette pratique suppose en général un grand moment de réflexion et n'est réellement pas recommendée dans un script shell. [111]

Exemple 24.15. Récursion en utilisant une variable locale

#!/bin/bash

#               facteurs
#               ---------


# Bash permet-il la récursion ?
# Eh bien, oui, mais...
# C'est si lent que vous devrez vous accrocher pour y arriver.


MAX_ARG=5
E_MAUVAIS_ARGS=65
E_MAUVAISE_ECHELLE=66


if [ -z "$1" ]
then
  echo "Usage : `basename $0` nombre"
  exit $E_MAUVAIS_ARGS
fi

if [ "$1" -gt $MAX_ARG ]
then
  echo "En dehors de l'échelle (5 est le maximum)."
  # Maintenant, allons-y.
  #  Si vous souhaitez une échelle plus importante, réécrivez-le dans un vrai
  #+ langage de programmation.
  exit $E_MAUVAISE_ECHELLE
fi  

fact ()
{
  local nombre=$1
  # La variable "nombre" doit être déclarée en local.
  # Sinon cela ne fonctionne pas.
  if [ "$nombre" -eq 0 ]
  then
    factoriel=1    # Le factoriel de 0 = 1.
  else
    let "decrnum = nombre - 1"
    fact $decrnum  # Appel à la fonction récursive (la fonction s'appelle elle-même).
    let "factoriel = $nombre * $?"
  fi

  return $factoriel
}

fact $1
echo "Le factoriel de $1 est $?."

exit 0

Voir aussi l'Exemple A.15, « Générer des nombres premiers en utilisant l'opérateur modulo » pour un exemple de récursion dans un script. Faites attention que la récursion demande beaucoup de ressources et s'exécute lentement. Son utilisation n'est donc pas appropriée dans un script.



[106] Néanmoins, ainsi que Thomas Braunberger nous le fait remarquer, une variable locale déclarée dans une fonction est également visible pour les fonctions appelées par elle.

#!/bin/bash
fonction1 ()
{
  local fonc1var=20
  echo "À l'intérieur de fonction1, \$fonc1var = $fonc1var."
  fonction2
}

fonction2 ()
{
  echo "À l'intérieur de fonction2, \$fonc1var = $fonc1var."
}

fonction1
exit 0

# Affichage produit par ce script:

# À l'intérieur de fonction1, $fonc1var = 20.
# À l'intérieur de fonction2, $fonc1var = 20.

Ce point est documenté dans le manuel de Bashnbsp;:

« Local ne peut être utilisé qu'à l'intérieur d'une fonction ; ce prédicat rend le nom de variable visible uniquement depuis la fonction ainsi que ses enfants. » [ soulignement ajouté ] Selon l'auteur du Guide ABS Guide, ce comportement est un bug.

[107] Autrement connue sous le nom de redondance.

[108] Autrement connue sous le nom de tautologie.

[109] Autrement connue sous le nom de métaphore.

[110] Autrement connue sous le nom de fonction récursive.

[111] Trop de niveaux de récursion pourrait arrêter brutalement un script avec une erreur de segmentation.

#!/bin/bash

#  Attention: Lancer ce script pourrait empêcher le bon fonctionnement de votre
#+ système !
#  Si vous êtes chanceux, il finira avec une erreur de segmentation avant
#+ d'avoir utiliser toute la mémoire disponible.

fonction_recursive ()
{
echo "$1"     # Fait en sorte que le fonction fait quelque chose et accélère le "segfault".
(( $1 < $2 )) && fonction_recursive $(( $1 + 1 )) $2;
#  Aussi longtemps que le premier paramètres est plus petit que le second,
#+ incrémente le premier et fait une récursion.
}

fonction_recursive 1 50000  # Récursion sur 50.000 niveaux!
#  Grande chance d'obtenir une erreur de segmentation (ceci dépendant de la
#+ taille de la pile, configurée avec ulimit -m).

#  Une récursion d'une telle profondeur peut même arrêter un programme C avec
#+ une erreur de segmentation, suite à l' utilisation de toute la mémoire
#+ allouée à la pile.

echo "Ceci ne s'affichera probablement pas."
exit 0  # Ce script ne finira par normalement.

# Merci, Stéphane Chazelas.