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 :
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 :
Cette ligne donne le droit d'exécution de votre script à tout le monde.
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écutionsource nom_du_script.shou. 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 :
Ci-dessus nous avons déclaré et initialisé 3 variables : Une chaîne de caractèrefoo, 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
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 :
- Déclaration de 2 variables
aetbinitialisées respectivement à10et12 - Addition dans
c - Affichage du contenu de
cà l'écran grâce à la commandeecho
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
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 :
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
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
Itérer sur une liste de valeurs
Parcourir des fichiers
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)
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 ...
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 :
unsetcrée des indices manquants - Préférer @ à * :
"${tableau[@]}"préserve les éléments séparés - Tableaux associatifs : Nécessitent
declare -Aet 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
- Bash Guide for Beginners
- Advanced Bash-Scripting Guide
- ShellCheck - Outil de vérification de scripts
- Explain Shell - Explique les commandes shell
Points clés à retenir
- Toujours commencer par
#!/bin/bash - Quoter les variables :
"$variable" - Vérifier les codes de retour avec
$? - Utiliser
set -euo pipefailpour un mode strict - Commenter votre code
- Tester vos scripts avant utilisation en production