Appearance
Scripts avancés Bash
Ce chapitre approfondit l'écriture de scripts Bash avec des structures de contrôle avancées, des fonctions avec paramètres et codes de retour, la gestion des tableaux (simples et associatifs), ainsi que la gestion des erreurs, des signaux et le débogage.
Structures de contrôle avancées
Bloc case
Le bloc case est l'équivalent du switch en d'autres langages. Il permet d'exécuter des commandes selon la valeur d'une variable.
bash
#!/usr/bin/env bash
read -rp "Choix (start|stop|status): " action
case $action in
start)
echo "Démarrage..."
;;
stop)
echo "Arrêt..."
;;
status)
echo "Statut: OK"
;;
*)
echo "Usage: start|stop|status" >&2
exit 1
;;
esacNotes:
- Chaque motif se termine par
;;(ou;&/;;&pour des comportements particuliers). - Les motifs acceptent les jokers (globs), ex.
start|run)ou*(par défaut).
read
read lit une ligne depuis l'entrée standard et l'assigne à des variables.
bash
read -rp "Votre nom: " nom
echo "Bonjour, $nom!"Options utiles
-r: ne pas interpréter les backslashes-p: afficher une invite-s: saisie silencieuse (ex. mot de passe)-tN: délai en secondes
select (menus interactifs)
select permet de générer facilement un menu interactif. Tant que le break n'est pas rencontré, le menu se répète après chaque choix.
bash
#!/usr/bin/env bash
PS3="Choisissez une option: "
select opt in Lister "Afficher date" Quitter; do
case $opt in
Lister)
ls -la
;;
"Afficher date")
date
;;
Quitter)
echo "Au revoir"; break
;;
*)
echo "Choix invalide" >&2
;;
esac
# break # Permet de sortir du menu après un choix
donePS3définit le prompt du menu.selectré-assigne la variable choisie à chaque itération.- Utilisez
breakpour sortir.
Tableaux
Tableaux indexés
Déclaration élément par élément:
bash
fruits[0]=pomme
fruits[1]=banane
fruits[2]=kiwiDéclaration en liste:
bash
fruits=(pomme banane kiwi)Accès et parcours:
bash
echo "Premier: ${fruits[0]}" # pomme
echo "Taille: ${#fruits[@]}" # 3
echo "Tous: ${fruits[@]}" # pomme banane kiwi
echo "Indices: ${!fruits[@]}" # 0 1 2
# Parcourir un tableau
for x in "${fruits[@]}"; do
echo "- $x"
done
# - pomme
# - banane
# - kiwi@ permet de lister le contenu du tableau pour tous les indices. ! quand joint avec @ permet de lister l’ensemble des indices du tableau.
Concaténation:
bash
autres=(mangue orange)
tous=("${fruits[@]}" "${autres[@]}")Tableaux associatifs
Un tableau associatif utilise des chaînes comme indices, par opposition aux tableaux indexés qui utilisent des entiers.
Il faut déclarer un tableau associatif avec declare -A:
bash
declare -A ages
ages[alice]=30
ages[bob]=25
echo "Âge d'alice: ${ages[alice]}"
echo "Clés: ${!ages[@]}"
echo "Valeurs: ${ages[@]}"
for k in "${!ages[@]}"; do
printf "%s: %s\n" "$k" "${ages[$k]}"
doneExemple (compteur de mots d’un fichier):
bash
declare -A count
while IFS= read -r line; do
for w in $line; do
((count[$w]++))
done
done < input.txt
for w in "${!count[@]}"; do
echo "$w: ${count[$w]}"
doneIFS (Internal Field Separator)
IFS est une variable spéciale qui définit les caractères utilisés pour séparer les champs lors de la lecture d'une ligne ou du traitement d'une liste.
Par défaut, IFS contient un espace, une tabulation et un saut de ligne.
Exemple de lecture ligne par ligne:
bash
while IFS= read -r line; do
# traitement de la ligne
done < fichier.txtread -r line: lecture d’une ligne brute,-rempêche l’interprétation des backslashes. (\n, \t, etc. restent littéraux),IFS=: En écrivantIFS=, on vide temporairementIFSpour la commande read. Doncreadne va pas couper la ligne en plusieurs mots selon les espaces, il prend toute la ligne telle quelle.while IFS= read -r line; do ... done < fichier.txt: "Tant que je peux lire une ligne brute de l’entrée standard, sans découper sur les espaces et sans interpréter les backslashes, je la mets dans la variablelineet j’exécute le corps de la boucle."
Options aide
Proposez une aide (-h|--help).
bash
usage() {
cat <<'EOF'
Usage: script.sh [-n NOM] fichier
-n NOM Saluer NOM
-h Aide
EOF
}
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
# Reste du codeAttraper les erreurs et signaux
trap ERR (gestion d’erreurs)
La commande trap permet d’exécuter une fonction ou une commande lors de la réception d’un signal ou d’un événement (comme une erreur).
bash
#!/usr/bin/env bash
set -Eeuo pipefail
on_error() {
echo "Erreur à la ligne $1 (statut=$2)" >&2
}
trap 'on_error ${LINENO} $?' ERR
false # simule une erreurExplications:
set -e: quitte sur erreur non testée.-E: propage ERR dans les fonctions.-u: interdit l’usage de variables non définies.-o pipefail: propage les erreurs dans les pipes.LINENO: numéro de ligne courantERR: déclenché lors d'une erreur siset -eest activé
trap sur signaux (SIGINT/SIGTERM)
Tous les signaux pouvant être envoyés par kill sauf SIGKILL (9) et SIGSTOP (19) peuvent être interceptés.
Par exemple, SIGTERM intercepte lorsqu’un utilisateur essaie d’arrêter l’exécution d’un script par un moyen conventionnel, par exemple CTRL-C.
En Bash, on écrit classiquement les noms SANS le préfixe SIG: INT, TERM, HUP...
bash
cleanup() {
echo "Nettoyage..."; rm -f /tmp/tmp.$$ 2>/dev/null || true
}
trap cleanup INT TERM EXIT
echo "Travail..."; sleep 10Notes:
INT(2) (Ctrl-C)TERM(15) (arrêt)EXIT(toute sortie)
EXIT n’est pas un "vrai" signal, c’est un pseudo‑événement déclenché à la fin du shell/script.
Utilisez‑les pour libérer des ressources, fermer des fichiers temporaires, etc.
trap -l liste les signaux disponibles, comme kill -l.
Débogage
Mode trace (-x)
Pour activer le mode debug en bash, la commande set –x peut être utilisée dans un script.
Si cette commande est incluse dans un script, les commandes seront imprimées dans le terminal ce qui est utile pour débugger.
bash
set -x # active la trace
commande
set +x # désactiveDans le shebang: #!/usr/bin/env bash -x pour activer dès le lancement.
Astuces
- Ajoutez des logs contextuels:
printf '[%s] %s\n' "$(date +%T)" "msg". - Isolez la partie fautive avec
set -x/set +x. - Tracez les valeurs clés:
declare -p var, cela affiche le contenu et le type de la variable.
Bonnes pratiques
- Shebang portable:
#!/usr/bin/env bash. set -Eeuo pipefailen tête pour les scripts critiques.- Évitez les chemins codés en dur; utilisez des variables et testez l’existence (
[[ -e path ]]). - Donnez des messages d’erreur clairs sur
stderret des codes de sortie explicites.
Pratique 💃
- Menu avec
selectproposant: Lister (ls -la), Date, Uptime, Quitter. Implémentez chaque option.
bash
#!/usr/bin/env bash
PS3="Choisissez une option: "
select opt in Lister "Afficher date" Uptime Quitter; do
case $opt in
Lister)
ls -la
;;
"Afficher date")
date
;;
Uptime)
uptime
;;
Quitter)
echo "Au revoir"; break
;;
*)
echo "Choix invalide" >&2
;;
esac
done- Écrivez
calc.shqui prendadd|sub|mul|divet 2 nombres. Utilisezcase, validez les arguments, gérez division par zéro (code de retour ≠ 0).
bash
#!/usr/bin/env bash
set -euo pipefail
if [[ $# -ne 3 ]]; then
echo "Usage: $0 {add|sub|mul|div} num1 num2" >&2
exit 1
fi
operation=$1
num1=$2
num2=$3
case $operation in
add)
result=$((num1 + num2))
;;
sub)
result=$((num1 - num2))
;;
mul)
result=$((num1 * num2))
;;
div)
if [[ $num2 -eq 0 ]]; then
echo "Erreur: division par zéro" >&2
exit 1
fi
result=$((num1 / num2))
;;
*)
echo "Opération invalide. Utilisez add, sub, mul, ou div." >&2
exit 1
;;
esac
echo "Résultat: $result"- Implémentez
is_dir(fonction) qui retourne 0 si son argument est un dossier, 1 sinon. Écrivez un script qui parcourt"$@"et affiche le type.
bash
is_dir() {
local path=$1
[[ -d $path ]]
}
for p in "$@"; do
if is_dir "$p"; then
echo "$p est un dossier"
else
echo "$p n'est pas un dossier"
fi
doneQuiz 🎉
Ce quiz couvre: case/read/select, fonctions, tableaux, traps et debug.
0 questions - Bonne chance !