9.5. Références indirectes

Supposez que la valeur d'une variable soit le nom d'une seconde variable. Est-il possible de retrouver la valeur de cette deuxième variable à partir de la première ? Par exemple, si a=lettre_de_l_alphabet et lettre_de_l_alphabet=z, est-ce qu'une référence à a pourrait renvoyer z ? En fait, c'est possible et cela s'appelle une référence indirecte. On utilise la notation inhabituelle eval var1=\$$var1.

Exemple 9.24. Références indirectes aux variables

#!/bin/bash
# index-ref.sh : Référencement de variable indirecte.
# Accéder au contenu du contenu d'une variable.

a=lettre_de_l_alphabet # La variable a contient le nom d'une autre variable.
lettre_de_l_alphabet=z

echo

# Référence directe.
echo "a = $a"          # a = lettre_de_l_alphabet

# Référence indirecte.
eval a=\$$a
echo "Maintenant, a = $a" # Maintenant, a = z

echo


# Maintenant, essayons de changer la référence du deuxième.

t=tableau_cellule_3
tableau_cellule_3=24
echo "\"tableau_cellule_3\" = $tableau_cellule_3"  # "tableau_cellule_3" = 24
echo -n "\"t\" déréférencé  = "; eval echo \$$t    # "t" déréférencé  = 24
# Dans ce cas simple, ce qui suit fonctionne aussi (pourquoi ?).
#   eval t=\$$t; echo "\"t\" = $t"

echo

t=tableau_cellule_3
NOUVELLE_VALEUR=387
tableau_cellule_3=$NOUVELLE_VALEUR
echo "Modification de la valeur de \"tableau_cellule_3\" en $NOUVELLE_VALEUR."
echo "\"tableau_cellule_3\" vaut maintenant $tableau_cellule_3"
echo -n "\"t\" déréférencé maintenant "; eval echo \$$t
# "eval" prend deux arguments "echo" et "\$$t" (valeur égale à $tableau_cellule_3)

echo

# (Merci, Stéphane Chazelas, pour la clarification sur le comportement ci-dessus.)


#  Une autre méthode est la notation ${!t}, discutée dans la section
#+ "Bash, version 2".
#  Voir aussi ex78.sh.

exit 0

Quel est l'utilité du référencement indirect des variables ? Cela donne à Bash une partie de la fonctionnalité des pointeurs, comme en C, par exemple dans la recherche dans des tables. Et, cela a aussi quelques autres applications intéressantes...

Nils Radtke montre comment construire des noms de variables « dynamiques » et comment évaluer leur contenu. Ceci peut être utile lors de l'intégration de fichiers de configuration.

#!/bin/bash


# ---------------------------------------------
# Ceci pourrait être "récupéré" d'un fichier séparé.
isdnMonFournisseurDistant=172.16.0.100
isdnTonFournisseurDistant=10.0.0.10
isdnServiceInternet="MonFournisseur"
# ---------------------------------------------


netDistant=$(eval "echo \$$(echo isdn${isdnServiceInternet}Distant)")
netDistant=$(eval "echo \$$(echo isdnMonFournisseurDistant)")
netDistant=$(eval "echo \$isdnMonFournisseurDistant")
netDistant=$(eval "echo $isdnMonFournisseurDistant")

echo "$netDistant"    # 172.16.0.100

# ================================================================

#  Et cela devient encore meilleur.

#  Considérez l'astuce suivant étant donnée une variable nommée getSparc,
#+ mais sans variable getIa64 :

chkMirrorArchs () {
  arch="$1";
  if [ "$(eval "echo \${$(echo get$(echo -ne $arch |
        sed 's/^\(.\).*/\1/g' | tr 'a-z' 'A-Z'; echo $arch |
        sed 's/^.\(.*\)/\1/g')):-false}")" = true ]
  then
    return 0;
  else
    return 1;
  fi;
}

getSparc="true"
unset getIa64
chkMirrorArchs sparc
echo $?        # 0
               # True

chkMirrorArchs Ia64
echo $?        # 1
               # False

# Notes :
# ------
# Même la partie du nom de la variable à substituer est construite explicitement.
# Les paramètres des appels de chkMirrorArchs sont tous en minuscule.
# Le nom de la variable est composé de deux parties : "get" et "Sparc" . . .

Exemple 9.25. Passer une référence indirecte à awk

#!/bin/bash

# Une autre version du script "column totaler"
# qui ajoute une colonne spécifiée (de nombres) dans le fichier cible.
# Celui-ci utilise les références indirectes.

ARGS=2
E_MAUVAISARGS=65

if [ $# -ne "$ARGS" ] # Vérifie le bon nombre d'arguments sur la ligne de
                      # commande.
then
   echo "Usage: `basename $0` nomfichier numéro_colonne"
   exit $E_MAUVAISARGS
fi

nomfichier=$1
numero_colonne=$2

#===== Identique au script original, jusqu'à ce point =====#


# Un script multi-ligne est appelé par awk ' ..... '


# Début du script awk.
# ------------------------------------------------
awk "

{ total += \$${numero_colonne} # référence indirecte
}
END {
     print total
     }

     " "$nomfichier"
# ------------------------------------------------
# Fin du script awk.

# La référence de variable indirecte évite les problèmes de
# référence d'une variable shell à l'intérieur d'un script embarqué.
# Merci, Stephane Chazelas.


exit 0

[Attention]

Attention

Cette méthode de référence indirecte est un peu délicate. Si la variable de second ordre change de valeur, alors la variable de premier ordre doit être correctement déréférencée (comme sur l'exemple ci-dessus). Heureusement, la notation ${!variable} introduite avec la version 2 de Bash (voir l'Exemple 34.2, « Références de variables indirectes - la nouvelle façon » et l'Exemple A.24, « Encore plus sur les fonctions de hachage ») rend les références indirectes plus intuitives.