[ ... ]
Compatibilité POSIX
Utile si le script doit rester portable hors Bash strict, mais demande plus de précautions sur les guillemets et les opérateurs.
Informatique
Les conditions Bash ne sont pas seulement une syntaxe à mémoriser. Elles décident si un script continue, s’arrête, crée un fichier, relance un service ou choisit une branche de déploiement. Une ligne if mal pensée peut donc transformer une automatisation utile en erreur silencieuse. La bonne approche consiste à comprendre ce que Bash évalue vraiment : non pas une phrase logique abstraite, mais le code de retour d’une commande ou d’une expression. À partir de là, if, else et elif deviennent beaucoup plus simples à utiliser proprement.
En Bash, if exécute une commande, puis regarde son statut de sortie. Par convention Unix, 0 signifie succès et une autre valeur signale un échec ou un état faux. C’est cette règle qui permet de tester aussi bien une comparaison, l’existence d’un fichier ou le résultat d’une commande système.
La forme minimale reste directe. Le bloc situé après then s’exécute uniquement si la condition réussit, puis fi ferme la structure. Le mot fi, inverse de if, évite toute ambiguïté dans les scripts longs.
#!/usr/bin/env bash
if [[ -f "$HOME/.bashrc" ]]; then
echo "Configuration Bash trouvée"
fi
Ce premier exemple ne compare pas une phrase ; il vérifie que l’expression [[ -f "$HOME/.bashrc" ]] retourne un succès. Cette nuance compte, car elle autorise aussi des tests sur des commandes réelles. Un script peut ainsi vérifier si une sauvegarde a réussi, si un service répond ou si un répertoire est accessible avant d’aller plus loin, sans transformer chaque contrôle en variable intermédiaire.
La règle pratique : une condition Bash doit correspondre à une situation observable. Sinon, elle documente mal le risque.
Un else utile rend l’échec impossible à ignorer. Il transforme le cas anormal en décision visible dans les logs.
else sert à écrire le comportement attendu quand la condition principale est fausse. Ce n’est pas seulement un confort de lecture : dans un script d’exploitation, une branche d’échec explicite évite de continuer comme si tout allait bien, surtout quand le traitement suivant dépend d’un fichier, d’un service ou d’un droit système. Le message doit être assez précis pour guider la correction, pas seulement annoncer “erreur”, et le code de sortie doit rester exploitable.
Les points clés sur If, else et elseif sans confusion pour permettent de préciser if, else et elseif sans confusion pour écrire des conditions lisibles.
#!/usr/bin/env bash
config="/etc/mon-app/config.conf"
if [[ -r "$config" ]]; then
echo "Lecture de la configuration"
else
echo "Configuration absente ou illisible" >&2
exit 1
fi
Ici, le script ne se contente pas d’afficher un message. Il sort avec exit 1, ce qui permet à un outil d’orchestration, une tâche cron ou un pipeline CI de comprendre que le traitement a échoué. C’est souvent la différence entre un incident visible et un script qui produit une suite de résultats incohérents, notamment quand plusieurs automatisations s’enchaînent sans supervision humaine immédiate.
La branche else doit donc répondre à une vraie question : faut-il arrêter, créer une valeur par défaut, demander une correction ou essayer un autre chemin ? Une bonne condition ne cache pas l’échec ; elle le rend exploitable.
elif évite de transformer une décision simple en escalier d’imbrications. Le script reste lisible de haut en bas.
Il signifie “sinon si” et permet de tester plusieurs conditions dans une seule chaîne. Bash lit les branches de haut en bas, puis exécute seulement la première condition vraie. Les branches suivantes sont ignorées, ce qui rend l’ordre des tests aussi important que leur syntaxe.
#!/usr/bin/env bash
usage=82
if (( usage >= 90 )); then
echo "Alerte critique"
elif (( usage >= 75 )); then
echo "Surveillance renforcée"
else
echo "Charge normale"
fi
L’ordre des tests est essentiel. Si le seuil 75 était placé avant le seuil 90, une valeur de 95 entrerait dans la mauvaise branche. Les conditions doivent aller du cas le plus spécifique au cas le plus général, surtout quand elles décrivent des seuils, des états ou des profils de risque.
Cette logique évite aussi le piège des if séparés. Trois blocs if indépendants peuvent tous s’exécuter si leurs conditions sont vraies. Une chaîne if / elif / else, elle, exprime une décision exclusive.
Compatibilité POSIX
Utile si le script doit rester portable hors Bash strict, mais demande plus de précautions sur les guillemets et les opérateurs.
Bash lisible
Pratique pour les chaînes, les motifs, les tests composés et les expressions plus robustes dans un script Bash assumé.
Calcul entier
Adapté aux compteurs, seuils numériques et expressions arithmétiques sans multiplier les opérateurs -gt ou -eq.
Le choix de syntaxe ne relève pas du goût personnel. Il dépend du niveau de portabilité attendu, de la nature du test et de la personne qui relira le script après incident. Les crochets simples [ ... ] correspondent au builtin test et restent utiles, mais ils demandent une syntaxe stricte : espaces autour des crochets, variables citées, opérateurs adaptés. Pour un script Bash assumé, [[ ... ]] rend souvent les conditions plus robustes et plus lisibles.
La différence se voit surtout avec les chaînes, les motifs et les combinaisons logiques. [[ ... ]] accepte mieux les expressions composées et limite certains pièges de découpage. Cela ne dispense pas de penser les cas limites, mais cela réduit le bruit syntaxique.
nom_fichier="rapport final.txt"
if [[ -f "$nom_fichier" && -r "$nom_fichier" ]]; then
echo "Le fichier peut être lu"
else
echo "Fichier absent ou non lisible"
fi
Pour les nombres entiers, (( ... )) est souvent plus naturel. Il permet d’écrire des comparaisons arithmétiques avec >, >= ou ==, sans passer par -gt, -ge ou -eq. Le résultat reste le même principe : succès ou échec.
[[ ... ]] quand le script cible clairement Bash et manipule des chaînes, fichiers ou conditions combinées.[ ... ] quand la portabilité POSIX est une contrainte réelle, pas une habitude héritée.(( ... )) pour les compteurs, seuils numériques et expressions arithmétiques simples.Une erreur fréquente consiste à comparer des nombres comme des chaînes, ou des chaînes comme des nombres. Avec [ ... ], les comparaisons numériques utilisent -eq, -ne, -lt, -le, -gt et -ge. Les chaînes utilisent plutôt =, !=, -z et -n.
Les tests de fichiers forment une autre famille. -f vérifie un fichier régulier, -d un répertoire, -r le droit de lecture, -w l’écriture et -x l’exécution. Ces opérateurs sont la base d’un script défensif, parce qu’ils contrôlent l’environnement avant d’agir.
| Besoin | Test courant | Exemple |
|---|---|---|
| Nombre supérieur à un seuil | (( valeur > seuil )) | (( usage > 80 )) |
| Chaîne vide | [[ -z "$nom" ]] | [[ -z "$env" ]] |
| Fichier lisible | [[ -r "$fichier" ]] | [[ -r "$config" ]] |
| Dossier existant | [[ -d "$chemin" ]] | [[ -d "$backup_dir" ]] |
Les guillemets restent une habitude saine dès qu’une variable représente une chaîne ou un chemin. Un nom de fichier peut contenir un espace, une variable peut être vide, et une valeur inattendue peut déplacer toute l’analyse. Citer les variables, c’est protéger la forme réelle de l’entrée.
Une condition n’est pas validée parce qu’elle marche une fois. Elle doit résister aux entrées vides, aux fichiers manquants et aux retours d’erreur.
La valeur peut-elle contenir un espace ?
Impact décision : Les guillemets évitent les découpages inattendus et les tests ambigus.
Que fait le script si aucun cas ne correspond ?
Impact décision : Un else explicite évite les sorties silencieuses.
Le cas le plus précis est-il évalué d’abord ?
Impact décision : Une branche générale placée trop tôt masque les cas particuliers.
La commande testée peut-elle échouer autrement ?
Impact décision : Un exit status non anticipé peut déclencher la mauvaise branche.
Le fichier ou dossier est-il vérifié avant usage ?
Impact décision : Les opérateurs -f, -d, -r ou -x évitent beaucoup d’échecs en chaîne.
Le script a-t-il été testé avec zéro, vide et valeur inconnue ?
Impact décision : Les bugs de conditions apparaissent souvent hors du scénario nominal.
Une condition composée doit rester relisible à froid. Sinon, le bug se cache souvent dans l’ordre des critères lors de la relecture.
Les opérateurs && et || permettent de combiner plusieurs tests. Dans [[ ... ]], ils se lisent naturellement : “si le fichier existe et qu’il est lisible”, “si la variable est vide ou inconnue”, etc. Leur usage devient problématique quand la condition porte trop d’intentions à la fois.
if [[ -f "$archive" && -r "$archive" && "$mode" == "restore" ]]; then
echo "Restauration possible"
else
echo "Conditions de restauration non réunies"
fi
Cette condition reste lisible parce qu’elle vérifie trois critères liés. Si vous ajoutez la disponibilité réseau, le rôle utilisateur, un seuil de taille et une option de ligne de commande, il vaut mieux découper. Des variables intermédiaires bien nommées peuvent transformer une condition opaque en contrat de lecture, à condition de rester proches du vocabulaire métier ou exploitation que l’équipe utilise déjà.
archive_ok=false
mode_ok=false
[[ -f "$archive" && -r "$archive" ]] && archive_ok=true
[[ "$mode" == "restore" ]] && mode_ok=true
if [[ "$archive_ok" == true && "$mode_ok" == true ]]; then
echo "Restauration possible"
fi
Ce style n’est pas nécessaire partout. Il devient utile quand le script doit être relu six mois plus tard, repris par une autre équipe ou exécuté dans un contexte sensible. En production, la lisibilité est une sécurité.
elif est excellent pour des seuils, des validations progressives ou quelques états. Mais quand vous comparez une même variable à de nombreuses valeurs textuelles, case devient souvent plus clair. C’est le cas des commandes start, stop, restart, ou des environnements dev, staging, prod.
commande="$1"
case "$commande" in
start)
echo "Démarrage"
;;
stop)
echo "Arrêt"
;;
restart)
echo "Redémarrage"
;;
*)
echo "Commande inconnue" >&2
exit 2
;;
esac
La branche *) joue le rôle du défaut. Elle est importante, car elle traite toutes les valeurs non prévues. Sans elle, un argument mal saisi peut traverser le script sans message utile. Là encore, le but n’est pas d’écrire plus de syntaxe, mais de créer un comportement explicite.
Ces deux réflexes évitent la majorité des conditions fragiles dans les scripts d’exploitation.
Tester la présence, les droits et la valeur attendue avant de modifier un fichier ou lancer une commande sensible.
Un argument inattendu doit produire un message clair et un code de sortie exploitable, pas traverser le script sans bruit.
Ce focus tardif sert de rappel opérationnel. Avant la dernière commande sensible, le script doit savoir refuser une entrée douteuse.
Le scénario nominal ne prouve presque rien. Il confirme le chemin heureux, pas la solidité des branches ni la réaction aux erreurs.
Un script Bash est souvent validé avec le cas idéal : le fichier existe, la variable est remplie, le réseau répond, l’utilisateur a les droits. Ce test ne suffit pas, car les conditions les plus dangereuses échouent rarement dans le chemin heureux. Elles cassent quand un argument manque, quand un chemin contient un espace, quand un droit disparaît ou quand une commande retourne un code inattendu, précisément au moment où l’automatisation devrait protéger l’équipe.
-r, -w ou -x selon l’action prévue.else ou un *) renvoie un message et un code de sortie.Pour aller plus loin, vous pouvez lancer le script avec bash -n pour détecter certaines erreurs de syntaxe, puis exécuter des scénarios contrôlés dans un répertoire temporaire. Cette méthode évite de tester directement sur un fichier réel ou un service actif. Elle force aussi à écrire des conditions qui décrivent précisément ce qui est autorisé et ce qui doit arrêter le traitement.
Voici un exemple volontairement simple : un script reçoit un chemin de configuration, vérifie qu’il existe, qu’il est lisible, puis choisit un comportement selon l’environnement. Les branches restent courtes, les erreurs sortent explicitement, et les valeurs inconnues sont refusées.
#!/usr/bin/env bash
set -u
config="${1:-}"
env_cible="${2:-dev}"
if [[ -z "$config" ]]; then
echo "Usage: script.sh chemin_config [dev|staging|prod]" >&2
exit 2
elif [[ ! -f "$config" ]]; then
echo "Le fichier de configuration est introuvable" >&2
exit 1
elif [[ ! -r "$config" ]]; then
echo "Le fichier de configuration n'est pas lisible" >&2
exit 1
fi
case "$env_cible" in
dev|staging)
echo "Validation standard pour $env_cible"
;;
prod)
echo "Validation renforcée avant production"
;;
*)
echo "Environnement inconnu" >&2
exit 2
;;
esac
Ce script illustre une règle simple : les conditions de garde arrivent d’abord, avant le traitement principal. Elles éliminent les entrées invalides, puis la suite peut se concentrer sur le vrai travail. Cette structure réduit les imbrications et rend le chemin nominal plus lisible.
Un bon bloc conditionnel Bash répond à trois questions. Que teste-t-on réellement ? Que fait-on si le test réussit ? Que fait-on si le test échoue ? Si plusieurs scénarios existent, elif ordonne les branches, mais il ne doit pas devenir un labyrinthe.
En pratique, privilégiez [[ ... ]] pour les scripts Bash modernes, (( ... )) pour l’arithmétique entière et case quand une même variable peut prendre plusieurs valeurs textuelles. Citez les variables, traitez les branches d’échec, retournez des codes explicites et testez les cas limites. Vos scripts deviendront plus prévisibles, plus simples à relire et beaucoup moins fragiles lors d’une automatisation réelle, sans chercher une sophistication inutile ni masquer l’intention derrière une condition compacte.
À lire aussi