Aller au contenu principal

Choix multiples : case et les tests avancés

Notions théoriques

Les limites d'une cascade de if

Vous savez déjà tester une variable avec if/elif/else. Mais dès qu'il faut comparer une seule variable à de nombreuses valeurs possibles (une sous-commande, une réponse utilisateur, une extension de fichier...), une longue cascade de elif devient vite difficile à lire. Bash propose une structure dédiée à ce cas précis : case.

La structure case

case "$variable" in
motif1)
# instructions
;;
motif2)
# instructions
;;
*)
# motif par défaut
;;
esac
  • case "$variable" in ouvre le bloc — pensez à quoter la variable, comme pour un [ ].
  • Chaque motif est suivi d'une parenthèse fermante ).
  • ;; marque la fin d'une branche : contrairement au switch d'autres langages, il n'y a pas de « fall-through » vers la branche suivante par défaut.
  • *) est le motif fourre-tout, équivalent du else — capte toute valeur non prévue par les motifs précédents.
  • esac (« case » à l'envers) ferme le bloc, comme fi ferme un if et done ferme une boucle.
info

Vous croiserez parfois ;& ou ;;& (Bash 4+), qui permettent de poursuivre l'exécution dans la branche suivante au lieu de sortir. C'est rare en pratique — la forme ;; reste la norme.

Les motifs d'un case

Un motif case accepte les caractères spéciaux du globbing, les mêmes que pour les noms de fichiers :

MotifSignification
*n'importe quelle chaîne (y compris vide)
?un seul caractère quelconque
[0-9]un seul chiffre
[Oo]uiOui ou oui
start|demarrerstart ou demarrer (alternative avec |)
case "$1" in
start|demarrer)
echo "Démarrage du service"
;;
stop|arreter)
echo "Arrêt du service"
;;
status)
echo "Statut du service"
;;
*)
echo "Commande inconnue : $1"
;;
esac

Un case est aussi très utile pour traiter une réponse read, où l'utilisateur peut taper plusieurs variantes valables :

read -p "Continuer ? (o/n) " reponse
case "$reponse" in
o|O|oui|Oui)
echo "On continue"
;;
*)
echo "Arrêt demandé"
exit 0
;;
esac

Le test [[ ]] : une alternative plus riche à [ ]

Nous avions annoncé en séance 4 du niveau 1 l'existence de [[ ]], une syntaxe de test spécifique à Bash (contrairement à [ ], disponible dans tous les shells POSIX). Elle apporte plusieurs avantages :

if [[ "$fichier" == *.tar.gz ]]; then
echo "C'est une archive"
fi
  • Pas de découpage de mots ni de globbing sur une variable non quotée à l'intérieur — [[ $fichier == *.tar.gz ]] fonctionne même sans guillemets, même si les garder reste une bonne habitude.
  • && et || s'utilisent directement à l'intérieur des crochets doubles : if [[ -d "$d" && -r "$d" ]].
  • Comparaison par motif avec == (comme dans l'exemple ci-dessus) — impossible avec [ ].
  • Comparaison par expression régulière avec =~ : if [[ "$ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] (nous resterons ici à un usage simple).
attention

[ ] reste utile à connaître : c'est la seule forme portable, disponible même dans des shells plus anciens que Bash (sh, dash). Pour un script destiné à rester en Bash, [[ ]] est aujourd'hui préférable au quotidien.

Exemple pratique

#!/bin/bash
# Script : service_local.sh
# Rôle : simuler la gestion d'un service (start/stop/status)

if [ $# -eq 0 ]; then
echo "Usage : $0 {start|stop|status|restart}" >&2
exit 1
fi

case "$1" in
start)
echo "Démarrage du service..."
;;
stop)
echo "Arrêt du service..."
;;
restart)
echo "Redémarrage du service..."
;;
status)
echo "Le service est actif."
;;
*)
echo "Commande inconnue : $1" >&2
echo "Usage : $0 {start|stop|status|restart}" >&2
exit 1
;;
esac
astuce

Remarquez la ressemblance avec les vieux scripts /etc/init.d/ : c'est exactement ce patron case sur $1 qu'ils utilisent pour interpréter start, stop, status...

Test de mémorisation/compréhension


Quel mot-clé ferme un bloc case en Bash ?


Que se passe-t-il si vous oubliez le ;; à la fin d'une branche case ?


Quel motif capte toutes les valeurs non prévues dans un case ?


Quel avantage apporte [[ ]] par rapport à [ ] ?


TP pour réfléchir et résoudre des problèmes

Nous reprenons sauvegarde.sh là où le niveau 1 l'avait laissé (version structurée en fonctions), en remplaçant sa confirmation par un case et en lui ajoutant un mode de fonctionnement.

Étape 1 — Remplacer la confirmation par un case


Bonne pratique - case plutôt qu'un if pour une réponse à choix multiples

Dès qu'une variable peut légitimement contenir plusieurs valeurs valides (o, O, oui...), un case reste plus lisible qu'un if chargé de multiples ||.

Étape 2 — Ajouter un mode de fonctionnement


Bonne pratique - Toujours prévoir le cas par défaut

Un case sans motif *) laisse passer silencieusement une valeur imprévue. Prévoyez toujours cette branche, ne serait-ce que pour afficher un message d'erreur explicite.

Étape 3 — Filtrer par extension avec [[ ]]


Bonne pratique - [[ ]] pour comparer un motif

[[ "$fichier" == *.log ]] est plus lisible que l'équivalent avec case pour un test isolé au milieu d'une boucle — réservez case aux choix à plusieurs branches, et [[ ]] aux tests ponctuels par motif.

📌 Une solution