Capitolo 34. Bash, versioni 2 e 3

Sommario
34.1. Bash, versione 2
34.2. Bash, versione 3

34.1. Bash, versione 2

La versione corrente di Bash, quella che viene eseguita sulla vostra macchina, attualmente è la 2.xx.y o la 3.xx.y.

bash$ echo $BASH_VERSION
2.05.b.0(1)-release
	      
La versione 2, aggiornamento del classico linguaggio di scripting di Bash, ha aggiunto gli array, [1] l'espansione di stringa e di parametro, e un metodo migliore per le referenziazioni indirette a variabili.

Esempio 34-1. Espansione di stringa

#!/bin/bash

# Espansione di stringa.
# Introdotta con la versione 2 di Bash.

#  Le stringhe nella forma $'xxx'
#+ consentono l'interpretazione delle sequenze di escape standard.

echo $'Tre segnali acustici \a \a \a'
     # Su alcuni terminali potrebbe venir eseguito un solo segnale acustico.
echo $'Tre form feed \f \f \f'
echo $'10 ritorni a capo \n\n\n\n\n\n\n\n\n\n'
echo $'\102\141\163\150'   # Bash
                           # Valori ottali di ciascun carattere.

exit 0

Esempio 34-2. Referenziazioni indirette a variabili - una forma nuova

#!/bin/bash

# Referenziazione indiretta a variabile.
# Possiede alcuni degli attributi delle referenziazioni del C++.


a=lettera_alfabetica
lettera_alfabetica=z

echo "a = $a"           # Referenziazione diretta.

echo "Ora a = ${!a}"    # Referenziazione indiretta.
#  La notazione ${!variabile} è di molto superiore alla vecchia
#+ "eval var1=\$$var2"

echo

t=cella_3
cella_3=24
echo "t = ${!t}"                             # t = 24
cella_3=387
echo "Il valore di t è cambiato in ${!t}"    # 387

#  È utile per il riferimento ai membri di un array o di una tabella,
#+ o per simulare un array multidimensionale.
#  Un'opzione d'indicizzazione sarebbe stata più gradita. Sigh.

exit 0

Esempio 34-3. Applicazione di un semplice database, con l'utilizzo della referenziazione indiretta

#!/bin/bash
#  resistor-inventory.sh
#  Applicazione di un semplice database che utilizza la referenziazione 
#+ indiretta alle variabili.

# ============================================================== #
# Dati

B1723_valore=470                              # Ohm
B1723_potenzadissip=.25                       # Watt
B1723_colori="giallo-viola-marrone"           # Colori di codice
B1723_loc=173                                 # Posizione
B1723_inventario=78                           # Quantità

B1724_valore=1000
B1724_potenzadissip=.25
B1724_colori="marrone-nero-rosso"
B1724_loc=24N
B1724_inventario=243

B1725_valore=10000
B1725_potenzadissip=.25
B1725_colori="marrone-nero-arancione"
B1725_loc=24N
B1725_inventario=89

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


echo

PS3='Inserisci il numero di catalogo: '

echo

select numero_catalogo in "B1723" "B1724" "B1725"
do
  Inv=${numero_catalogo}_inventario
  Val=${numero_catalogo}_valore
  Pdissip=${numero_catalogo}_potenzadissip
  Loc=${numero_catalogo}_loc
  Codcol=${numero_catalogo}_colori

  echo
  echo "Numero di catalogo $numero_catalogo:"
  echo "In magazzino ci sono ${!Inv} resistori da\
[${!Val} ohm / ${!Pdissip} watt]."        
  echo "Si trovano nel contenitore nr. ${!Loc}."
  echo "Il loro colore di codice è \"${!Codcol}\"."

  break
done

echo; echo

# Esercizi:
# --------
# 1) Riscrivete lo script in modo che legga i dati da un file esterno.
# 2) Riscrivete lo script utilizzando gli array, al posto della
#+   referenziazione indiretta a variabile.
#    Quale, tra i due, è il metodo più diretto e intuitivo?


# Nota:
# -----
#  Gli script di shell non sono appropriati per le applicazioni di database, 
#+ tranne quelle più semplici. Anche in questi casi, però, 
#+ bisogna ricorrere ad espedienti e trucchi vari. 
#  È molto meglio utilizzare un linguaggio che abbia un
#+ supporto nativo per le strutture, come C++ o Java (o anche Perl).

exit 0

Esempio 34-4. Utilizzo degli array e di vari altri espedienti per simulare la distribuzione casuale di un mazzo di carte a 4 giocatori

#!/bin/bash

# Carte:
# Distribuzione di un mazzo di carte a quattro giocatori.

NONDISTRIBUITA=0
DISTRIBUITA=1

GIÀ_ASSEGNATA=99

LIMITE_INFERIORE=0
LIMITE_SUPERIORE=51
CARTE_PER_SEME=13
CARTE=52

declare -a Mazzo
declare -a Semi
declare -a Carte
#  Sarebbe stato più semplice ed intuitivo
#+ con un unico array tridimensionale.
#  Forse una futura versione di Bash supporterà gli array multidimensionali.


Inizializza_Mazzo ()
{
i=$LIMITE_INFERIORE
until [ "$i" -gt $LIMITE_SUPERIORE ]
do
  Mazzo[i]=$NONDISTRIBUITA   #  Imposta ogni carta del "Mazzo" come non
                             #+ distribuita.
  let "i += 1"
done
echo
}

Inizializza_Semi ()
{
Semi[0]=F #Fiori
Semi[1]=Q #Quadri
Semi[2]=C #Cuori
Semi[3]=P #Picche
}

Inizializza_Carte ()
{
Carte=(2 3 4 5 6 7 8 9 10 J Q K A)
# Metodo alternativo di inizializzazione di array.
}

Sceglie_Carta ()
{
numero_carta=$RANDOM
let "numero_carta %= $CARTE"
if [ "${Mazzo[numero_carta]}" -eq $NONDISTRIBUITA ]
then
  Mazzo[numero_carta]=$DISTRIBUITA
  return $numero_carta
else
  return $GIÀ_ASSEGNATA
fi
}

Determina_Carta ()
{
numero=$1
let "numero_seme = numero / CARTE_PER_SEME"
seme=${Semi[numero_seme]}
echo -n "$seme-"
let "nr_carta = numero % CARTE_PER_SEME"
Carta=${Carte[nr_carta]}
printf %-4s $Carta
# Visualizza le carte ben ordinate per colonne.
}

Seme_Casuale ()  # Imposta il seme del generatore di numeri casuali.
{                # Cosa succederebbe se questo non venisse fatto?
Seme=`eval date +%s`
let "Seme %= 32766"
RANDOM=$Seme
#  Quali sono gli altri metodi per impostare il seme
#+ del generatore di numeri casuali?
}

Da_Carte ()
{
echo

carte_date=0
while [ "$carte_date" -le $LIMITE_SUPERIORE ]
do
  Sceglie_Carta
  t=$?

  if [ "$t" -ne $GIÀ_ASSEGNATA ]
  then
    Determina_Carta $t

    u=$carte_date+1
    # Ritorniamo all'indicizzazione in base 1 (temporaneamente). Perché?
    let "u %= $CARTE_PER_SEME"
    if [ "$u" -eq 0 ]   # Costrutto condizionale if/then annidato.
    then
     echo
     echo
    fi
    # Separa i giocatori.

    let "carte_date += 1"
  fi  
done  

echo

return 0
}


# Programmazione strutturata:
# l'intero programma è stato "modularizzato" per mezzo delle Funzioni.

#================
Seme_Casuale
Inizializza_Mazzo
Inizializza_Semi
Inizializza_Carte
Da_Carte
#================

exit 0




#  Esercizio 1:
#  Aggiungete commenti che spieghino completamente lo script.

#  Esercizio 2:
#  Aggiungete una routine (funzione) per visualizzare la distribuzione ordinata
#+ per seme.
#  Potete aggiungere altri fronzoli, si vi aggrada.

#  Esercizio 3:
#  Semplificate e raffinate la logica dello script.

Note

[1]

Chet Ramey ha promesso gli array associativi (una funzionalità Perl) in una futura release di Bash. Questo non è ancora avvenuto, neanche nella versione 3.