37. Bash, versions 2, 3 et 4

37.1. Bash, version 2

La version actuelle de Bash, celle que vous avez sur votre machine, est très probablement la version 2.xx.yy 3.xx.yy ou ou 4.xx.yy.

bash$ echo $BASH_VERSION
3.2.25(1)-release
              

La mise à jour de la version 2 du langage de script Bash classique ajoute les variables de type tableau, l'expansion de chaînes de caractères et de paramètres ainsi qu'une meilleure méthode pour les références de variables indirectes, entre autres fonctionnalités.

Exemple 37.1. Expansion d'une chaîne de caractères

#!/bin/bash

# Expansion de chaînes de caractères.
# Introduit avec la version 2 de Bash.

# Les chaînes de caractères de la forme $'xxx' ont les caractères d'échappement
# standard interprétés.

echo $'Trois cloches sonnant à la fois \a \a \a'
       # Pourrait sonner seulement une fois sur certains terminaux.
echo $'Trois retours chariot \f \f \f'
echo $'10 retours chariot \n\n\n\n\n\n\n\n\n\n'
echo $'\102\141\163\150'   # Bash
                           # Équivalent en octal des caractères.

exit 0

Exemple 37.2. Références de variables indirectes - la nouvelle façon

#!/bin/bash

# Référencement de variables indirectes.
# Ceci a quelques-uns des attributs du C++.


a=lettre_de_l_alphabet
lettre_de_l_alphabet=z

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

echo "Maintenant a = ${!a}"    # Référence indirecte.
# La notation ${!variable} est bien supérieure à l'ancien "eval var1=\$$var2"

echo

t=cellule_table_3
cellule_table_3=24
echo "t = ${!t}"        # t = 24
cellule_table_3=387
echo "La valeur de t a changé en ${!t}"    # 387

# Ceci est utile pour référencer les membres d'un tableau ou d'une table, 
# ou pour simuler un tableau multi-dimensionnel.
#  Une option d'indexage (analogue à un pointeur arithmétique) aurait été bien.
#+ Sigh.

exit 0

Exemple 37.3. Simple application de base de données, utilisant les références de variables indirectes

#!/bin/bash
# resistor-inventory.sh
# Simple base de données utilisant le référencement indirecte de variables.

# ============================================================== #
# Données

B1723_value=470                                   # Ohms
B1723_powerdissip=.25                             # Watts
B1723_colorcode="yellow-violet-brown"             # Bandes de couleurs
B1723_loc=173                                     # Où elles sont
B1723_inventory=78                                # Combien

B1724_value=1000
B1724_powerdissip=.25
B1724_colorcode="brown-black-red"
B1724_loc=24N
B1724_inventory=243

B1725_value=10000
B1725_powerdissip=.25
B1725_colorcode="brown-black-orange"
B1725_loc=24N
B1725_inventory=89

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


echo

PS3='Entrez le numéro du catalogue : '

echo

select numero_catalogue in "B1723" "B1724" "B1725"
do
  Inv=${numero_catalogue}_inventory
  Val=${numero_catalogue}_value
  Pdissip=${numero_catalogue}_powerdissip
  Loc=${numero_catalogue}_loc
  Ccode=${numero_catalogue}_colorcode

  echo
  echo "Catalogue numéro $numero_catalogue :"
  echo "Il existe ${!Inv} résistances de [${!Val} ohm / ${!Pdissip} watt] en stock."
  echo "Elles sont situées dans bin # ${!Loc}."
  echo "Leur code couleur est \"${!Ccode}\"."

  break
done

echo; echo

# Exercice :
# ---------
#   Réécrire ce script en utilisant des tableaux, plutôt qu'en utilisant le
#+ référencement indirecte des variables.
# Quelle méthode est plus logique et intuitive ?


# Notes :
# ------
#  Les scripts shells sont inappropriés pour tout, sauf des applications simples
#+ de base de données, et, même là, cela implique des astuces.
#  Il est bien mieux d'utiliser un langage supportant nativement les structures
#+ de données, tels que C++ ou Java (voire même Perl).

exit 0

Exemple 37.4. Utiliser des tableaux et autres astuces pour gérer quatre mains aléatoires dans un jeu de cartes

#!/bin/bash

# Cartes :
# Gère quatre mains d'un jeu de cartes.

NON_RECUPERE=0
RECUPERE=1

DUPE_CARD=99

LIMITE_BASSE=0
LIMITE_HAUTE=51
CARTES_DANS_SUITE=13
CARTES=52

declare -a Jeu
declare -a Suites
declare -a Cartes
#  Le script aurait été plus simple à implémenter et plus intuitif
#+ avec un seul tableau à trois dimensions.
# Peut-être qu'une future version de Bash gèrera des tableaux multi-dimensionnels.


initialise_Jeu ()
{
i=$LIMITE_BASSE
until [ "$i" -gt $LIMITE_HAUTE ]
do
  Jeu[i]=$NON_RECUPERE   # Initialise chaque carte d'un "Jeu" comme non récupérée.
  let "i += 1"
done
echo
}

initialise_Suites ()
{
Suites[0]=C #Carreaux
Suites[1]=D #Piques
Suites[2]=H #Coeurs
Suites[3]=S #Trèfles
}

initialise_Cartes ()
{
Cartes=(2 3 4 5 6 7 8 9 10 J Q K A)
# Autre méthode pour initialiser un tableau.
}

recupere_une_carte ()
{
numero_carte=$ALEATOIRE
let "numero_carte %= $CARTES"
if [ "${Jeu[numero_carte]}" -eq $NON_RECUPERE ]
then
  Jeu[numero_carte]=$RECUPERE
  return $numero_carte
else  
  return $DUPE_CARD
fi
}

analyse_carte ()
{
nombre=$1
let "suit_nombre = nombre / CARTES_DANS_SUITE"
suite=${Suites[suit_nombre]}
echo -n "$suit-"
let "no_carte = nombre % CARTES_DANS_SUITE"
Carte=${Cartes[no_carte]}
printf %-4s $Carte
# Affiche proprement les cartes.
}

recherche_nombre_aleatoire ()  # Générateur de nombres aléatoires.
{                              # Que se passe-t'il si vous ne faites pas cela ?
recherche=`eval date +%s`
let "recherche %= 32766"
ALEATOIRE=$recherche
# Quelles sont les autres méthodes de génération de nombres aléatoires ?
}

gere_cartes ()
{
echo

cartes_recuperees=0
while [ "$cartes_recuperees" -le $LIMITE_HAUTE ]
do
  recupere_une_carte
  t=$?

  if [ "$t" -ne $DUPE_CARD ]
  then
    analyse_carte $t

    u=$cartes_recuperees+1
    # Retour à un indexage simple (temporairement). Pourquoi ?
    let "u %= $CARTES_DANS_SUITE"
    if [ "$u" -eq 0 ]   # Condition if/then imbriquée.
    then
     echo
     echo
    fi
    # Mains séparées.

    let "cartes_recuperees += 1"
  fi  
done  

echo

return 0
}


# Programmation structurée :
# La logique entière du programme est modularisée en fonctions.

#================
recherche_nombre_aleatoire
initialise_Jeu
initialise_Suites
initialise_Cartes
gere_cartes
#================

exit 0



# Exercice 1 :
# Ajouter des commentaires détaillées de ce script.

# Exercice 2 :
# Ajouter une routine (fonction) pour afficher chaque main triée par suite.
# Vous pouvez ajouter d'autres fonctionnalités suivant vos souhaits.

# Exercice 3 :
# Simplifier et améliorer la logique du script.