Aller au contenu

Réalisation de scripts Bash

1 - Éditer et lancer un script

Pour éditer votre script Bash dans l'environnement GNU/Linux deux options s'offrent à vous :

  • Utiliser un éditeur de texte en ligne de commande tel que nano ou vim.
  • Utiliser un éditeur de texte graphique comme VSCode

Une fois votre éditeur ouvert, la première chose à écrire est :

#!/bin/bash

Cette ligne de code indique au terminal quel type de Shell il doit utiliser, dans notre cas nous utiliserons le Shell Bash. Si vous souhaitez avoir des informations sur les différents types de Shell, je ne peux que vous conseiller d'aller voir la page Wikipédia suivante : lien

Ensuite il faut avant tout enregistrer votre nouveau fichier de script, pour cela donnez lui l'extension .sh, extension utilisée pour les scripts Bash.

Pour lancer un script et observer son fonctionnement, positionnez vous avec le terminal à l'endroit où il est situé (voir commandes cd et ls), puis entrez les commandes suivantes :

$ chmod a+x nom_du_script.sh

Cette ligne donne le droit d'exécution de votre script à tout le monde.

$ ./nom_du_script.sh
Cette ligne lance votre script.

Autres façons d'exécuter un script

  • bash nom_du_script.sh : Lance le script avec bash même sans droits d'exécution
  • source nom_du_script.sh ou . nom_du_script.sh : Exécute le script dans le shell courant (utile pour définir des variables d'environnement)

2 - Utilisation de variables

2.1 - Déclaration

Pour déclarer une variable en shell Bash il suffit rentrer son nom puis sa valeur initiale :

foo="J'aime la Moussaka"
bar=42
test=1.618033
Ci-dessus nous avons déclaré et initialisé 3 variables : Une chaîne de caractère foo, un entier bar et un flottant test. Remarquez qu'il n'est pas nécessaire de déclarer le type de la variable, Bash va automatiquement le faire suivant valeur initiale choisie.

Warning

Lors de la déclaration d'une variable attention à ne pas mettre d'espace entre la fin du nom et le =

2.2 - Assignation vs Utilisation : comprendre le symbole $

C'est une règle fondamentale en Bash qu'il faut bien comprendre :

🔴 Sans $ = ASSIGNATION (écrire dans la variable)

nom="Alice"        # On ASSIGNE la valeur "Alice" à nom
age=25             # On ASSIGNE la valeur 25 à age
resultat=100       # On ASSIGNE la valeur 100 à resultat

🟢 Avec $ = UTILISATION (lire le contenu de la variable)

echo $nom          # On LIT et affiche le contenu de nom
calcul=$age        # On LIT age et copie sa valeur dans calcul
total=$resultat    # On LIT resultat et copie sa valeur dans total

Erreur courante

# ❌ FAUX : Bash va essayer d'exécuter "Alice" comme une commande !
$nom="Alice"

# ✅ CORRECT : On assigne la valeur
nom="Alice"

Exemple complet pour bien comprendre :

#!/bin/bash

# ASSIGNATION (pas de $)
prenom="Jean"
nom="Dupont"

# UTILISATION (avec $)
nom_complet="$prenom $nom"    # On LIT prenom et nom
echo "Bonjour $nom_complet"   # On LIT nom_complet

# Copie de variable
copie=$nom                     # On LIT nom et écrit dans copie
echo $copie                    # Affiche "Dupont"

Analogie mathématique

C'est comme en mathématiques :

  • x = 5 → assignation (pas de $ en Bash : x=5)
  • y = x → utilisation de x (avec $ en Bash : y=$x)

2.3 - Utilisation pratique des variables

Pour utiliser le contenu d'une variable il faut mettre le symbole $ devant le nom de celle-ci, on fait alors référence à sa case mémoire, son contenu.

  • Faire des calculs avec les variables :

Une syntaxe particulière est utilisée pour faire faire un calcul entre 2 ou plusieurs variables, exemple de script ci-dessous :

#!/bin/bash
a=10
b=12
c=$((a+b))

# Afficher le résultat à l'écran
echo $c

  1. Déclaration de 2 variables a et b initialisées respectivement à 10 et 12
  2. Addition dans c
  3. Affichage du contenu de c à l'écran grâce à la commande echo

Warning

Bash ne permet pas directement de faire des additions avec des nombres décimaux. Ci nécessaire installer l'application appelée bc permettant de gérer le calcul en nombres flottants.

  • Concaténer des chaînes de caractères

Bash permet de concaténer rapidement le contenu de 2 variables de type chaîne de caractères, un exemple ci-dessous :

#!/bin/bash
a="J'aime "
b="coder avec le shell Bash !" 
c=$a$b

# Afficher le résultat à l'écran
echo $c
1. Déclaration de 2 variables a et b initialisées respectivement à J'aime et coder avec le shell Bash 2. Concaténation dans c 3. Affichage du contenu de c à l'écran grâce à la commande echo

2.3 - Variables spéciales

Bash dispose de variables spéciales prédéfinies très utiles :

Variable Description
$? Code de retour de la dernière commande (0 = succès, autre = erreur)
$$ PID du script en cours
$! PID du dernier processus lancé en arrière-plan
$# Nombre de paramètres positionnels
$@ Tous les paramètres sous forme de mots séparés
$* Tous les paramètres en une seule chaîne
$USER Nom de l'utilisateur courant
$HOME Répertoire personnel de l'utilisateur
$PWD Répertoire de travail actuel
$IFS Internal Field Separator (séparateur de champs)
#!/bin/bash
echo "Script lancé par : $USER"
echo "Répertoire courant : $PWD"
echo "Nombre de paramètres : $#"
echo "Tous les paramètres : $@"

Focus sur IFS (Internal Field Separator)

IFS est une variable spéciale qui définit quels caractères sont utilisés pour séparer les mots/champs en Bash.

Valeur par défaut : IFS contient trois caractères : espace, tabulation et retour à la ligne (\n)

Pourquoi c'est important :

#!/bin/bash

# Exemple 1 : Découper une chaîne avec read
# IFS définit comment découper la chaîne
IFS=',' read -ra fruits <<< "pomme,banane,orange"
echo "${fruits[0]}"  # pomme
echo "${fruits[1]}"  # banane

# Exemple 2 : Problème sans modifier IFS
texte="pomme verte,banane,orange"
IFS=' ' read -ra items <<< "$texte"
# Résultat : ["pomme", "verte,banane,orange"] - découpe sur l'espace !

# Exemple 3 : Solution avec IFS correct
IFS=',' read -ra items <<< "$texte"
# Résultat : ["pomme verte", "banane", "orange"] - découpe sur la virgule !

Cas d'usage pratique : Tri de tableaux

#!/bin/bash

fruits=("pomme verte" "banane" "orange rouge")

# ❌ PROBLÈME : Sans modifier IFS
# Les éléments avec espaces sont séparés incorrectement
sorted=($(sort <<<"${fruits[*]}"))
# Résultat incorrect : ["banane", "orange", "pomme", "rouge", "verte"]

# ✅ SOLUTION : Modifier IFS temporairement
IFS=$'\n' sorted=($(sort <<<"${fruits[*]}"))
unset IFS  # Remettre IFS à sa valeur par défaut
# Résultat correct : ["banane", "orange rouge", "pomme verte"]

Bonne pratique : Sauvegarder et restaurer IFS

#!/bin/bash

# Sauvegarder l'ancien IFS
OLD_IFS=$IFS

# Modifier IFS pour un traitement spécifique
IFS=','
read -ra data <<< "val1,val2,val3"

# Restaurer l'ancien IFS
IFS=$OLD_IFS

# Ou plus simplement : unset IFS

Attention

Toujours remettre IFS à sa valeur d'origine après utilisation, sinon cela peut causer des comportements inattendus dans le reste de votre script !

Exemple complet : Traiter un fichier CSV

#!/bin/bash

# Fichier CSV : nom,age,ville
# Alice,25,Paris
# Bob,30,Lyon

while IFS=',' read -r nom age ville; do
    echo "Nom: $nom, Age: $age, Ville: $ville"
done < donnees.csv

2.4 - Portée des variables et export

#!/bin/bash

# Variable locale au script
ma_variable="locale"

# Variable d'environnement (accessible aux processus enfants)
export MA_VAR_ENV="globale"

# Lancer un sous-shell
bash -c 'echo $ma_variable'  # N'affiche rien
bash -c 'echo $MA_VAR_ENV'   # Affiche "globale"

2.5 - Manipulation de chaînes

Bash offre des fonctionnalités puissantes pour manipuler les chaînes :

#!/bin/bash
texte="Bonjour le monde"

# Longueur de la chaîne
echo ${#texte}  # Affiche 16

# Extraction de sous-chaîne (position:longueur)
echo ${texte:0:7}  # Affiche "Bonjour"

# Remplacement
echo ${texte/monde/Bash}  # Affiche "Bonjour le Bash"

# Majuscules/minuscules
echo ${texte^^}  # BONJOUR LE MONDE
echo ${texte,,}  # bonjour le monde

3 - Les structures alternatives

3.1 Les opérateurs de comparaison

Opérateur Nom
-lt lower than (inférieur à)
-gt greater than (supérieur à)
-eq equal (égal à)
-ne not equal (différent de)
-ge greater or equal (supérieur ou egal à)
-le lower or equal (inférieur ou égal à)

Info

Ces opérateurs sont uniquement utilisables avec des nombres, pour les chaines de caractères voir ci-dessous. De plus Bash ne permet pas directement de comparer des nombres décimaux, ces expressions fonctionnent seulement avec des entiers.

Opérateur Nom
== Égalité
!= Différence

3.2 - Opérateurs logiques et tests de fichiers

Opérateurs logiques

# ET logique
if [ $a -gt 5 ] && [ $a -lt 10 ]; then
    echo "a est entre 5 et 10"
fi

# OU logique
if [ $a -eq 1 ] || [ $a -eq 2 ]; then
    echo "a vaut 1 ou 2"
fi

# NON logique
if [ ! -f fichier.txt ]; then
    echo "Le fichier n'existe pas"
fi

Tests sur les fichiers

Test Description
-e fichier Le fichier existe
-f fichier C'est un fichier régulier
-d fichier C'est un répertoire
-r fichier Le fichier est lisible
-w fichier Le fichier est modifiable
-x fichier Le fichier est exécutable
-s fichier Le fichier n'est pas vide
-L fichier C'est un lien symbolique
#!/bin/bash
if [ -f "/etc/passwd" ]; then
    echo "Le fichier passwd existe"
fi

if [ -d "/home" ]; then
    echo "/home est un répertoire"
fi

3.3 If

La structure if effectue le test donné, si il est vrai, elle dirige l'exécution vers le then, dans le cas contraire elle continu la suite des instructions, un script exemple ci-dessous :

#!/bin/bash

# Capture des 2 nombres saisis par l'utilisateur
echo "Rentrez le premier nombre"
read a # Saisi du nombre a
echo "Rentrez le second nombre"
read b # Saisi du nombre b

# Test d'infériorité de a sur b
if [ $a -lt $b ]; then
    echo "a est inférieur à b"
fi

# Test de supériorité de a sur b
if [ $a -gt $b ]; then
    echo "a est supérieur à b"
fi

# Test d'égalité de a et b
if [ $a -eq $b ]; then
    echo "a est égal à b"
fi

Warning

Toujours mettre un espace après le 1er [ et avant le dernier ] de la condition.

Double crochets [[ ]]

Bash supporte aussi la syntaxe [[ ]] qui est plus moderne et plus permissive :

if [[ $a -lt $b && $c -gt 5 ]]; then
    echo "Conditions multiples simplifiées"
fi

3.4 If Else

Nous reprenons le script précédent en utilisant une structure if elif :

#!/bin/bash

# Capture des 2 nombres saisis par l'utilisateur
echo "Rentrez le premier nombre"
read a # Saisi du nombre a
echo "Rentrez le second nombre"
read b # Saisi du nombre b


if [ $a -lt $b ]; then # Test d'infériorité de a sur b
    echo "a est inférieur à b"
elif [ $a -gt $b ]; then  # Sinon test de supériorité de a sur b
    echo "a est supérieur à b"
elif [ $a -eq $b ]; then # Sinon test d'égalité de a et b
    echo "a est égal à b"
fi

3.5 Case

La structure case très utilisée dans les menus, permet d'orienter le programme suivant un choix de l'utilisateur :

#!/bin/bash
echo "Rentrez un nombre compris entre 1 et 4"
read nb # Saisi du nombre nb

case $nb in # Tests sur la variable nb, contenant le nombre saisi
    1) echo "Super ! Vous avez saisi 1";; # Ne pas oublier les 2 ;; sauf sur la dernière ligne
    2) echo "Formidable ! Vous avez saisi 2";;
    3) echo "Fantastique ! Vous avez saisi 3";;
    4) echo "Wahoo ! Vous avez saisi 4";;
    *) echo "Vous n'avez rien saisi de valable ! Dommage" # Cas où nb different de [1;4]
esac

Utilisation avancée du case

case $choix in
    [oO]|[yY]es) echo "Oui confirmé";;  # Multiple patterns
    [nN]o) echo "Non refusé";;
    [1-5]) echo "Nombre entre 1 et 5";;  # Plage
    *.txt) echo "Fichier texte";;  # Pattern matching
    *) echo "Choix non reconnu";;
esac

4 - Les boucles

4.1 While

La boucle while ou "tant que" permet de répéter un ensemble d'instructions tant que la condition donnée est vraie, exemple ci-dessous :

#!/bin/bash

i=0 # Initialisation de la variable de comptage à 0
while [ $i -lt 5 ]; do # On tourne tant que i<5
    echo "compteur = "$i # On affiche le contenu de i
    i=$(($i+1)) # On incrémente i
done 

4.2 Until

La boucle until ou "jusqu'à" contrairement à la boucle while va s'exécuter jusqu'à ce que la condition soit vérifiée. Nous reprenons l'exemple précédent et adaptons la condition pour compter de 0 à 4 :

#!/bin/bash

i=0 # Initialisation de la variable de comptage à 0
until [ $i -gt 5 ]; do # On tourne jusqu'à ce que i>4
    echo "compteur = "$i # On affiche le contenu de i
    i=$(($i+1)) # On incrémente i
done 

4.3 For

Les boucles for en Bash permettent d'itérer sur des listes d'éléments. Voici les principales façons de les utiliser :

Syntaxe de base

for variable in liste_elements
do
    # commandes à exécuter
done

Itérer sur une liste de valeurs

for fruit in pomme banane orange
do
    echo "J'aime les ${fruit}s"
done

Parcourir des fichiers

for fichier in *.txt
do
    echo "Traitement de $fichier"
    cat "$fichier"
done

Utiliser une séquence de nombres

# Syntaxe avec accolades
for i in {1..5}
do
    echo "Itération numéro $i"
done

# Ou avec la commande seq
for i in $(seq 1 10)
do
    echo "Nombre : $i"
done

Style C (boucle for en style C/Cpp)

for ((i=0; i<5; i++))
do
    echo "Compteur : $i"
done
Exemple : Parcours des fichiers d'un dossier

Ci-dessous nous listons tous les fichier du répertoire courant soit /home/foo/ :

#!/bin/bash

i=0 # Initialisation de la variable de comptage à 0
# On parcourt le répertoire actuel, le nom du fichier recueilli est mis dans la var nom_fichier à chaque itération 
# '*' Désigne l'ensemble des fichier du répertoire
for nom_fichier in *; do 
    echo $i". "$nom_fichier
    i=$(($i+1)) # On incrémente i
done

Astuces utiles

On peut utiliser break pour sortir de la boucle prématurément, continue permet de passer à l'itération suivante.

5 - Les paramètres positionnels

Les paramètres positionnels sont les paramètres passés en entrée d'un script quand vous appelez celui-ci dans le terminal. Par exemple lors de l'utilisation de la commande rm on peut passer l'option -r qui est un paramètre positionnel : rm -r

Il est facile d'utiliser ces paramètres en Bash ils sont référencés par un numéro : 1 pour le premier paramètre, 2 pour le second ...

$ mon_script param1 param2 param3
Pour utiliser ces paramètres dans le script :

#!/bin/bash

$1 # Utilisation du premier paramètre
$2 # Utilisation du second paramètre
$3 # Utilisation du troisième paramètre

Info

Le paramètre positionnel $0 est une chaîne de caractères contenant le nom du script

5.1 - Vérification des paramètres

Il est important de vérifier la présence et la validité des paramètres :

#!/bin/bash

# Vérifier le nombre de paramètres
if [ $# -lt 2 ]; then
    echo "Usage: $0 param1 param2"
    exit 1
fi

echo "Premier paramètre : $1"
echo "Deuxième paramètre : $2"
echo "Tous les paramètres : $@"

5.2 - Valeurs par défaut

#!/bin/bash

# Définir une valeur par défaut si le paramètre n'est pas fourni
nom=${1:-"Invité"}
age=${2:-18}

echo "Bonjour $nom, vous avez $age ans"

6 - Les fonctions

Les fonctions permettent de structurer et réutiliser le code :

6.1 - Déclaration et appel

#!/bin/bash

# Déclaration d'une fonction
ma_fonction() {
    echo "Hello depuis ma fonction !"
}

# Appel de la fonction
ma_fonction

6.2 - Fonctions avec paramètres

#!/bin/bash

afficher_somme() {
    local a=$1  # local rend la variable locale à la fonction
    local b=$2
    local resultat=$((a + b))
    echo "La somme de $a et $b est : $resultat"
}

# Appel avec paramètres
afficher_somme 5 3

6.3 - Retour de valeur

#!/bin/bash

calculer_double() {
    local nombre=$1
    local resultat=$((nombre * 2))
    echo $resultat  # "Retourne" via echo
}

# Capturer le retour
mon_double=$(calculer_double 10)
echo "Le double est : $mon_double"

7 - Gestion des erreurs

7.1 - Codes de retour

#!/bin/bash

# Vérifier le succès d'une commande
if ls /chemin/inexistant 2>/dev/null; then
    echo "Répertoire trouvé"
else
    echo "Erreur : répertoire introuvable"
fi

# Vérifier avec $?
mkdir /tmp/test
if [ $? -eq 0 ]; then
    echo "Répertoire créé avec succès"
fi

7.2 - Mode strict

#!/bin/bash

# Arrêter le script en cas d'erreur
set -e

# Traiter les variables non définies comme des erreurs
set -u

# Faire échouer les pipelines si une commande échoue
set -o pipefail

# Combinaison recommandée
set -euo pipefail

7.3 - Trap pour gérer les signaux

#!/bin/bash

# Fonction de nettoyage
cleanup() {
    echo "Nettoyage avant sortie..."
    rm -f /tmp/fichier_temporaire
}

# Exécuter cleanup à la sortie du script
trap cleanup EXIT

# Votre code ici
echo "Script en cours d'exécution"

8 - Entrées/Sorties et redirections

8.1 - Lecture interactive avancée

#!/bin/bash

# Lecture avec timeout (5 secondes)
read -t 5 -p "Entrez votre nom (5s max) : " nom

# Lecture silencieuse (pour les mots de passe)
read -s -p "Mot de passe : " password
echo  # Nouvelle ligne après le mot de passe

# Lecture d'un seul caractère
read -n 1 -p "Appuyez sur une touche..."

8.2 - Redirections

#!/bin/bash

# Rediriger la sortie standard vers un fichier
echo "Texte" > fichier.txt    # Écrase le fichier
echo "Ajout" >> fichier.txt   # Ajoute au fichier

# Rediriger les erreurs
commande 2> erreurs.log       # Seulement les erreurs
commande &> tout.log          # Sortie standard et erreurs
commande 2>&1                 # Rediriger erreurs vers sortie standard

# Lire depuis un fichier
while read ligne; do
    echo "Lu : $ligne"
done < fichier.txt

9 - Les structures de données : Tableaux et listes

Bash propose deux types principaux de tableaux : les tableaux indexés (listes ordonnées) et les tableaux associatifs (dictionnaires/hash maps).

9.1 - Tableaux indexés (listes)

9.1.1 - Déclaration et initialisation

#!/bin/bash

# Méthode 1 : Déclaration avec valeurs
fruits=("pomme" "banane" "orange" "kiwi")

# Méthode 2 : Déclaration vide puis ajout
declare -a animaux
animaux[0]="chat"
animaux[1]="chien"
animaux[2]="oiseau"

# Méthode 3 : Initialisation avec des indices non consécutifs
nombres[0]=10
nombres[5]=50
nombres[10]=100

# Méthode 4 : À partir d'une commande
fichiers=($(ls *.txt))

# Méthode 5 : Lecture depuis une chaîne
IFS=',' read -ra couleurs <<< "rouge,vert,bleu"

9.1.2 - Accès aux éléments

#!/bin/bash

fruits=("pomme" "banane" "orange" "kiwi" "mangue")

# Accéder à un élément par son index (commence à 0)
echo ${fruits[0]}      # pomme
echo ${fruits[2]}      # orange

# Dernier élément
echo ${fruits[-1]}     # mangue (Bash 4.3+)
echo ${fruits[@]: -1}  # mangue (compatible toutes versions)

# Accès avec valeur par défaut si l'index n'existe pas
echo ${fruits[10]:-"N/A"}  # Affiche "N/A"

# Tous les éléments
echo ${fruits[@]}      # pomme banane orange kiwi mangue
echo ${fruits[*]}      # pomme banane orange kiwi mangue

# Différence entre @ et * avec quotes
for f in "${fruits[@]}"; do echo "$f"; done  # Chaque élément séparé
for f in "${fruits[*]}"; do echo "$f"; done  # Tous en une seule chaîne

9.1.3 - Propriétés et informations

#!/bin/bash

fruits=("pomme" "banane" "orange" "kiwi")

# Nombre d'éléments
echo ${#fruits[@]}     # 4
echo ${#fruits[*]}     # 4

# Longueur d'un élément spécifique
echo ${#fruits[1]}     # 6 (longueur de "banane")

# Lister tous les indices utilisés
echo ${!fruits[@]}     # 0 1 2 3

# Vérifier si un index existe
if [ -v fruits[2] ]; then
    echo "L'index 2 existe"
fi

9.1.4 - Modification et manipulation

#!/bin/bash

fruits=("pomme" "banane" "orange")

# Ajouter à la fin
fruits+=("kiwi")
fruits+=("mangue" "fraise")

# Modifier un élément
fruits[1]="ananas"

# Insérer à un index spécifique
fruits[10]="cerise"  # Crée des "trous" dans le tableau

# Supprimer un élément
unset fruits[2]      # Supprime "orange"

# Supprimer tout le tableau
unset fruits

# Copier un tableau
fruits_copy=("${fruits[@]}")

# Concaténer deux tableaux
tableau1=("a" "b" "c")
tableau2=("d" "e" "f")
combine=("${tableau1[@]}" "${tableau2[@]}")
echo ${combine[@]}   # a b c d e f

9.1.5 - Extraction de sous-tableaux (slicing)

#!/bin/bash

nombres=(10 20 30 40 50 60 70 80 90 100)

# Extraire à partir d'un index (index:longueur)
echo ${nombres[@]:2:3}    # 30 40 50 (à partir de l'index 2, 3 éléments)

# Extraire depuis un index jusqu'à la fin
echo ${nombres[@]:5}      # 60 70 80 90 100

# Extraire les N derniers éléments
echo ${nombres[@]: -3}    # 80 90 100 (3 derniers)

# Créer un nouveau tableau avec un slice
sous_tableau=("${nombres[@]:3:4}")
echo ${sous_tableau[@]}   # 40 50 60 70

9.1.6 - Tri et opérations avancées

#!/bin/bash

# Tri d'un tableau (en utilisant une boucle)
fruits=("orange" "pomme" "banane" "kiwi")

# Méthode 1 : Tri avec sort
IFS=$'\n' sorted=($(sort <<<"${fruits[*]}"))
unset IFS
echo ${sorted[@]}  # banane kiwi orange pomme

# Méthode 2 : Tri numérique
nombres=(5 2 8 1 9 3)
IFS=$'\n' sorted_nums=($(sort -n <<<"${nombres[*]}"))
unset IFS
echo ${sorted_nums[@]}  # 1 2 3 5 8 9

# Inverser un tableau
original=("a" "b" "c" "d")
reversed=()
for ((i=${#original[@]}-1; i>=0; i--)); do
    reversed+=("${original[i]}")
done
echo ${reversed[@]}  # d c b a

# Filtrer les éléments (garder seulement ceux qui correspondent)
mots=("chat" "chien" "oiseau" "poisson" "cheval")
avec_ch=()
for mot in "${mots[@]}"; do
    if [[ $mot == ch* ]]; then
        avec_ch+=("$mot")
    fi
done
echo ${avec_ch[@]}  # chat chien cheval

9.1.7 - Parcours et itération

#!/bin/bash

fruits=("pomme" "banane" "orange" "kiwi")

# Méthode 1 : Parcourir les valeurs
for fruit in "${fruits[@]}"; do
    echo "Fruit : $fruit"
done

# Méthode 2 : Parcourir avec les indices
for i in "${!fruits[@]}"; do
    echo "Index $i : ${fruits[$i]}"
done

# Méthode 3 : Style C avec compteur
for ((i=0; i<${#fruits[@]}; i++)); do
    echo "Position $i : ${fruits[$i]}"
done

# Méthode 4 : Avec while
i=0
while [ $i -lt ${#fruits[@]} ]; do
    echo "Item $i : ${fruits[$i]}"
    ((i++))
done

9.1.8 - Recherche dans un tableau

#!/bin/bash

fruits=("pomme" "banane" "orange" "kiwi" "mangue")

# Vérifier si un élément existe
recherche="orange"
trouve=false

for fruit in "${fruits[@]}"; do
    if [ "$fruit" = "$recherche" ]; then
        trouve=true
        break
    fi
done

if $trouve; then
    echo "$recherche trouvé !"
else
    echo "$recherche non trouvé"
fi

# Trouver l'index d'un élément
for i in "${!fruits[@]}"; do
    if [ "${fruits[$i]}" = "orange" ]; then
        echo "Index de orange : $i"
        break
    fi
done

# Compter les occurrences
nombres=(1 2 3 2 4 2 5)
compte=0
for n in "${nombres[@]}"; do
    [ "$n" -eq 2 ] && ((compte++))
done
echo "Le nombre 2 apparaît $compte fois"

9.2 - Tableaux associatifs (dictionnaires)

Les tableaux associatifs utilisent des chaînes comme clés au lieu d'indices numériques. Disponibles depuis Bash 4.0+.

9.2.1 - Déclaration et initialisation

#!/bin/bash

# Déclaration obligatoire avec -A
declare -A ages

# Méthode 1 : Affectation individuelle
ages["Alice"]=25
ages["Bob"]=30
ages["Charlie"]=35

# Méthode 2 : Initialisation en une ligne
declare -A couleurs=(
    ["rouge"]="#FF0000"
    ["vert"]="#00FF00"
    ["bleu"]="#0000FF"
)

# Méthode 3 : À partir de paires clé=valeur
declare -A config
while IFS='=' read -r key value; do
    config["$key"]="$value"
done < config.txt

9.2.2 - Accès et manipulation

#!/bin/bash

declare -A personne=(
    ["nom"]="Dupont"
    ["prenom"]="Jean"
    ["age"]="35"
    ["ville"]="Paris"
)

# Accéder à une valeur
echo ${personne["nom"]}        # Dupont
echo ${personne["age"]}        # 35

# Modifier une valeur
personne["age"]=36

# Ajouter une nouvelle paire
personne["profession"]="Ingénieur"

# Supprimer une clé
unset personne["ville"]

# Vérifier si une clé existe
if [ -v personne["nom"] ]; then
    echo "La clé 'nom' existe"
fi

# Avec valeur par défaut
echo ${personne["telephone"]:-"Non renseigné"}

9.2.3 - Parcours des tableaux associatifs

#!/bin/bash

declare -A notes=(
    ["Math"]=15
    ["Physique"]=14
    ["Français"]=16
    ["Histoire"]=13
)

# Parcourir toutes les clés
echo "=== Liste des matières ==="
for matiere in "${!notes[@]}"; do
    echo "- $matiere"
done

# Parcourir toutes les valeurs
echo "=== Liste des notes ==="
for note in "${notes[@]}"; do
    echo "- $note/20"
done

# Parcourir clés et valeurs ensemble
echo "=== Bulletin de notes ==="
for matiere in "${!notes[@]}"; do
    echo "$matiere : ${notes[$matiere]}/20"
done

# Calculer une moyenne
total=0
count=0
for note in "${notes[@]}"; do
    total=$((total + note))
    ((count++))
done
moyenne=$((total / count))
echo "Moyenne : $moyenne/20"

9.2.4 - Informations sur les tableaux associatifs

#!/bin/bash

declare -A capitale=(
    ["France"]="Paris"
    ["Allemagne"]="Berlin"
    ["Italie"]="Rome"
    ["Espagne"]="Madrid"
)

# Nombre d'éléments
echo "Nombre de pays : ${#capitale[@]}"

# Lister toutes les clés
echo "Pays : ${!capitale[@]}"

# Lister toutes les valeurs
echo "Capitales : ${capitale[@]}"

# Vérifier si vide
if [ ${#capitale[@]} -eq 0 ]; then
    echo "Le tableau est vide"
else
    echo "Le tableau contient ${#capitale[@]} éléments"
fi

9.3 - Tableaux multidimensionnels (simulation)

Bash ne supporte pas nativement les tableaux multidimensionnels, mais on peut les simuler :

9.3.1 - Avec des tableaux associatifs

#!/bin/bash

# Simuler une matrice avec des clés "ligne,colonne"
declare -A matrice

# Remplir une matrice 3x3
for i in {0..2}; do
    for j in {0..2}; do
        matrice["$i,$j"]=$((i * 3 + j))
    done
done

# Afficher la matrice
echo "=== Matrice 3x3 ==="
for i in {0..2}; do
    for j in {0..2}; do
        printf "%3d " ${matrice["$i,$j"]}
    done
    echo
done

# Accéder à un élément spécifique
echo "Élément [1,2] = ${matrice["1,2"]}"

9.3.2 - Avec des tableaux de tableaux (en utilisant nameref)

#!/bin/bash

# Créer plusieurs tableaux
declare -a ligne1=(1 2 3)
declare -a ligne2=(4 5 6)
declare -a ligne3=(7 8 9)

# Tableau de noms de tableaux
lignes=("ligne1" "ligne2" "ligne3")

# Parcourir le "tableau 2D"
for nom_ligne in "${lignes[@]}"; do
    declare -n ref=$nom_ligne  # nameref : référence au tableau
    echo "Ligne : ${ref[@]}"
done

9.4 - Cas d'usage pratiques

9.4.1 - Gestion d'une liste de tâches

#!/bin/bash

declare -a taches=()

ajouter_tache() {
    taches+=("$1")
    echo "Tâche ajoutée : $1"
}

lister_taches() {
    if [ ${#taches[@]} -eq 0 ]; then
        echo "Aucune tâche"
        return
    fi

    echo "=== Liste des tâches ==="
    for i in "${!taches[@]}"; do
        echo "$((i+1)). ${taches[$i]}"
    done
}

supprimer_tache() {
    local index=$((${1} - 1))
    if [ $index -ge 0 ] && [ $index -lt ${#taches[@]} ]; then
        echo "Tâche supprimée : ${taches[$index]}"
        unset taches[$index]
        # Réindexer le tableau
        taches=("${taches[@]}")
    else
        echo "Index invalide"
    fi
}

# Utilisation
ajouter_tache "Faire les courses"
ajouter_tache "Appeler le médecin"
ajouter_tache "Finir le rapport"
lister_taches
supprimer_tache 2
lister_taches

9.4.2 - Compteur de fréquence de mots

#!/bin/bash

declare -A frequence

texte="le chat est sur le tapis le chat dort"

# Compter les occurrences de chaque mot
for mot in $texte; do
    ((frequence[$mot]++))
done

# Afficher les résultats
echo "=== Fréquence des mots ==="
for mot in "${!frequence[@]}"; do
    echo "$mot : ${frequence[$mot]} fois"
done

9.4.3 - Gestion de configuration

#!/bin/bash

declare -A config=(
    ["host"]="localhost"
    ["port"]="8080"
    ["debug"]="true"
    ["max_connections"]="100"
)

# Fonction pour lire la configuration
get_config() {
    local key=$1
    echo "${config[$key]}"
}

# Fonction pour modifier la configuration
set_config() {
    local key=$1
    local value=$2
    config[$key]=$value
    echo "Configuration mise à jour : $key=$value"
}

# Sauvegarder dans un fichier
save_config() {
    local file=$1
    echo "# Configuration générée le $(date)" > "$file"
    for key in "${!config[@]}"; do
        echo "$key=${config[$key]}" >> "$file"
    done
}

# Utilisation
echo "Port actuel : $(get_config port)"
set_config port 9090
save_config "app.conf"

9.4.4 - Cache simple

#!/bin/bash

declare -A cache

get_from_cache() {
    local key=$1
    if [ -v cache["$key"] ]; then
        echo "Cache HIT : $key"
        echo "${cache[$key]}"
        return 0
    else
        echo "Cache MISS : $key" >&2
        return 1
    fi
}

set_cache() {
    local key=$1
    local value=$2
    cache["$key"]=$value
    echo "Mis en cache : $key"
}

# Simulation d'une fonction coûteuse
fonction_couteuse() {
    local param=$1
    echo "Calcul en cours pour $param..." >&2
    sleep 2  # Simulation d'un traitement long
    echo "Résultat pour $param"
}

# Fonction avec cache
get_with_cache() {
    local key=$1

    if ! get_from_cache "$key"; then
        local result=$(fonction_couteuse "$key")
        set_cache "$key" "$result"
        echo "$result"
    fi
}

# Utilisation
echo "=== Premier appel (lent) ==="
get_with_cache "user123"

echo "=== Deuxième appel (rapide) ==="
get_with_cache "user123"

9.5 - Bonnes pratiques avec les tableaux

Conseils

  • Toujours quoter : Utilisez "${tableau[@]}" et non ${tableau[@]}
  • Vérifier l'existence : Utilisez [ -v tableau[index] ] avant d'accéder
  • Attention aux trous : unset crée des indices manquants
  • Préférer @ à * : "${tableau[@]}" préserve les éléments séparés
  • Tableaux associatifs : Nécessitent declare -A et Bash 4.0+
#!/bin/bash

# ❌ Mauvais : sans quotes
fruits=("pomme verte" "banane" "orange")
for f in ${fruits[@]}; do
    echo $f  # Sépare "pomme" et "verte"
done

# ✅ Bon : avec quotes
for f in "${fruits[@]}"; do
    echo "$f"  # Garde "pomme verte" ensemble
done

# ✅ Vérification avant accès
if [ -v tableau[5] ]; then
    echo "${tableau[5]}"
else
    echo "Index 5 n'existe pas"
fi

10 - Expressions régulières

#!/bin/bash

email="user@example.com"

# Test avec =~
if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
    echo "Email valide"
else
    echo "Email invalide"
fi

# Extraction avec les groupes
texte="Date: 2025-11-10"
if [[ $texte =~ ([0-9]{4})-([0-9]{2})-([0-9]{2}) ]]; then
    annee=${BASH_REMATCH[1]}
    mois=${BASH_REMATCH[2]}
    jour=${BASH_REMATCH[3]}
    echo "Année: $annee, Mois: $mois, Jour: $jour"
fi

11 - Bonnes pratiques

11.1 - Style et lisibilité

#!/bin/bash

# Utiliser des noms de variables explicites
nombre_utilisateurs=10
nom_fichier_config="/etc/app.conf"

# Commenter le code complexe
# Cette boucle traite tous les fichiers log de plus de 30 jours
for log in /var/log/*.log; do
    # Vérifier l'âge du fichier
    if [ $(find "$log" -mtime +30) ]; then
        echo "Archivage de $log"
    fi
done

# Indenter correctement
if [ condition ]; then
    if [ autre_condition ]; then
        commande
    fi
fi

11.2 - Sécurité

#!/bin/bash

# Toujours quoter les variables pour éviter les problèmes avec les espaces
fichier="mon fichier.txt"
if [ -f "$fichier" ]; then  # Bien
    cat "$fichier"
fi

# Éviter d'utiliser eval avec des entrées utilisateur
# DANGER : eval "$commande_utilisateur"

# Utiliser des chemins absolus pour les commandes critiques
/usr/bin/rm -f /tmp/fichier  # Plutôt que : rm -f /tmp/fichier

11.3 - Déboggage

#!/bin/bash

# Activer le mode debug (affiche chaque commande)
set -x

# Commandes à débogger
echo "Debug activé"
variable="test"

# Désactiver le mode debug
set +x

# Ou lancer le script avec : bash -x script.sh

12 - Exemples pratiques

12.1 - Script de sauvegarde

#!/bin/bash
set -euo pipefail

SOURCE="/home/user/documents"
DESTINATION="/backup"
DATE=$(date +%Y%m%d_%H%M%S)
ARCHIVE="backup_$DATE.tar.gz"

echo "Début de la sauvegarde..."
tar -czf "$DESTINATION/$ARCHIVE" "$SOURCE" 2>/dev/null

if [ $? -eq 0 ]; then
    echo "Sauvegarde réussie : $ARCHIVE"
else
    echo "Erreur lors de la sauvegarde" >&2
    exit 1
fi

12.2 - Menu interactif

#!/bin/bash

while true; do
    clear
    echo "=== MENU PRINCIPAL ==="
    echo "1. Afficher la date"
    echo "2. Lister les fichiers"
    echo "3. Afficher l'usage disque"
    echo "4. Quitter"
    echo "======================"
    read -p "Votre choix : " choix

    case $choix in
        1)
            date
            read -p "Appuyez sur Entrée pour continuer..."
            ;;
        2)
            ls -lh
            read -p "Appuyez sur Entrée pour continuer..."
            ;;
        3)
            df -h
            read -p "Appuyez sur Entrée pour continuer..."
            ;;
        4)
            echo "Au revoir !"
            exit 0
            ;;
        *)
            echo "Choix invalide"
            sleep 2
            ;;
    esac
done

12.3 - Traitement de fichier CSV

#!/bin/bash

CSV_FILE="data.csv"

# Lire le fichier ligne par ligne
while IFS=',' read -r nom age ville; do
    echo "Nom: $nom, Age: $age, Ville: $ville"
done < "$CSV_FILE"

13 - Ressources et aide

13.1 - Commandes utiles

# Obtenir de l'aide sur une commande
man bash          # Manuel complet de Bash
help if           # Aide sur les structures Bash
type commande     # Type d'une commande
which commande    # Chemin d'une commande

13.2 - Sites de référence


Points clés à retenir

  • Toujours commencer par #!/bin/bash
  • Quoter les variables : "$variable"
  • Vérifier les codes de retour avec $?
  • Utiliser set -euo pipefail pour un mode strict
  • Commenter votre code
  • Tester vos scripts avant utilisation en production