9.5. Referenziazione indiretta delle variabili

Ipotizziamo che il valore di una variabile sia il nome di una seconda variabile. È in qualche modo possibile recuperare il valore di questa seconda variabile dalla prima? Per esempio, se a=lettera_alfabeto e lettera_alfabeto=z, può una referenziazione ad a restituire z? In effetti questo è possibile e prende il nome di referenziazione indiretta. Viene utilizzata l'insolita notazione eval var1=\$$var2.

Esempio 9-23. Referenziazioni indirette

#!/bin/bash
# ind-ref.sh: Referenziazione indiretta a variabile.
# Accedere al contenuto del contenuto di una variabile.

a=lettera_alfabeto   # La variabile "a" contiene il nome di un'altra variabile.
lettera_alfabeto=z

echo

# Referenziazione diretta.
echo "a = $a"        # a = lettera_alfabeto

# Referenziazione indiretta.
eval a=\$$a
echo "Ora a = $a"    # Ora a = z

echo


# Proviamo a modificare la referenziazione di secondo-ordine.

t=tabella_cella_3
tabella_cella_3=24
echo "\"tabella_cella_3\" = $tabella_cella_3"      # "tabella_cella_3" = 24
echo -n "\"t\" dereferenziata = "; eval echo \$$t  # "t" dereferenziata = 24
# In questo semplice caso, funziona anche quello che segue (perché?).
#   eval t=\$$t; echo "\"t\" = $t"

echo

t=tabella_cella_3
NUOVO_VAL=387
tabella_cella_3=$NUOVO_VAL
echo "Valore di \"tabella_cella_3\" modificato in $NUOVO_VAL."
echo "\"tabella_cella_3\" ora $tabella_cella_3"
echo -n "\"t\" dereferenziata  "; eval echo \$$t
# "eval" ha due argomenti "echo" e "\$$t" (impostata a $tabella_cella_3)

echo

# (Grazie a Stephane Chazelas, per aver chiarito il comportamento precedente.)


#  Un altro metodo è quello della notazione ${!t}, trattato nella 
#+ sezione "Bash, versione 2". Vedi anche ex78.sh.

exit 0

Qual'è l'utilità pratica della referenziazione indiretta delle variabili? Fornire a Bash un po' delle funzionalità dei puntatori del C, ad esempio, nella ricerca nelle tabelle. Nonché avere qualche altra interessantissima applicazione. . . .

Nils Radtke mostra come realizzare nomi di variabili "dinamici" e valutarne il contenuto. Questo può risultare utile quando occorre "includere" dei file di configurazione con source.

#!/bin/bash


# ---------------------------------------------------------------------
#  Questo file può essere "caricato" da un altro file tramite "source".
isdnMioProviderReteRemota=172.16.0.100
isdnTuoProviderReteRemota=10.0.0.10
isdnServizioOnline="MioProvider"
# ---------------------------------------------------------------------
      

reteRemota=$(eval "echo \$$(echo isdn${isdnServizioOnline}ReteRemota)")
reteRemota=$(eval "echo \$$(echo isdnMioProviderReteRemota)")
reteRemota=$(eval "echo \$isdnMioProviderReteRemota")
reteRemota=$(eval "echo $isdnMioProviderReteRemota")

echo "$reteRemota"   # 172.16.0.100

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

#  E fa ancor meglio.

#  Considerate il frammento seguente dove viene inizializzata una 
#+ variabile di nome getSparc, ma manca getIa64:

verMirrorArch () { 
  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')):-falso}")" = vero ]
  then
     return 0;
  else
     return 1;
  fi;
}

getSparc="vero"
unset getIa64
verMirrorArch sparc
echo $?        # 0
               # Vero

verMirrorArch Ia64
echo $?        # 1
               # Falso

# Note:
# ----
#  Anche la parte del nome della variabile da-sostituire viene costruita 
#+ esplicitamente.
#  I parametri passati a verMirrorArch sono in lettere minuscole.
#  Il nome della variabile è formato da due parti: "get" e "Sparc" . . .

Esempio 9-24. Passare una referenziazione indiretta a awk

#!/bin/bash

#  Altra versione dello script "column totaler"
#+ che aggiunge una colonna (contenente numeri) nel file di destinazione.
#  Qui viene utilizzata la referenziazione indiretta.

ARG=2
E_ERR_ARG=65

if [ $# -ne "$ARG" ] #  Verifica il corretto nr. di argomenti da riga
                     #+ di comando.
then
   echo "Utilizzo: `basename $0` nomefile numero_colonna"
   exit $E_ERR_ARG
fi

nomefile=$1
numero_colonna=$2

#===== Fino a questo punto è uguale all'originale =====#


# Script awk di più di una riga vengono invocati con   awk ' ..... '


# Inizio script awk.
# ----------------------------------------------------------
awk "

{ totale += \$${numero_colonna} # referenziazione indiretta
}
END {
     print totale
     }

     " "$nomefile"
# ------------------------------------------------
# Fine script awk.

#  La referenziazione indiretta evita le difficoltà della referenziazione
#+ di una variabile di shell all'interno di uno script awk incorporato.
#  Grazie, Stephane Chazelas.

exit 0

Attenzione

Questo metodo è un po' complicato. Se la seconda variabile modifica il proprio valore, allora la prima deve essere correttamente dereferenziata (come nell'esempio precedente). Fortunatamente, la notazione ${!variabile}, introdotta con la versione 2 di Bash (vedi Esempio 34-2 e Esempio A-23), rende la referenziazione indiretta più intuitiva.