Linux sans magie #30 — Codes de retour : comprendre $?, succès, erreur et enchaînement des commandes
Comprendre les codes de retour sous Bash, lire $?, distinguer succès et échec, et utiliser &&, || et ; pour enchaîner des commandes proprement.
Quand une commande Linux se termine, elle ne produit pas seulement du texte à l’écran. Elle renvoie aussi un statut de sortie, souvent appelé exit status ou code de retour.
Ce nombre permet au shell de savoir si la commande a réussi ou non.
C’est ce mécanisme qui rend possibles des constructions comme :
commande1 && commande2
ou :
commande1 || commande2
Avant d’écrire des conditions ou des scripts Bash plus complets, il faut comprendre cette logique. Dans ce #30, nous allons observer les codes de retour, lire $? et voir comment &&, || et ; changent l’enchaînement des commandes.
Un code de retour n’est pas un message affiché
Une commande peut afficher quelque chose sur la sortie standard ou sur la sortie d’erreur, mais son code de retour est une information différente.
Dans Bash, la convention générale est :
0signifie succès ;- une valeur non nulle signifie échec ou résultat négatif selon la sémantique de la commande.
Le sens exact d’un code non nul dépend donc du programme.
Par exemple, GNU grep utilise normalement :
0lorsqu’au moins une ligne correspond ;1lorsqu’aucune ligne ne correspond ;2lorsqu’une erreur se produit.
Cela montre pourquoi « non nul » ne veut pas toujours dire « panne grave ». Pour grep, le code 1 peut simplement signifier que le motif recherché n’existe pas dans l’entrée.
Lire le dernier statut avec $?
Bash expose le statut de la commande ou du pipeline précédent avec le paramètre spécial $?.
Commençons par deux commandes intégrées très simples :
true
echo "$?"
Résultat attendu :
0
Puis :
false
echo "$?"
Résultat attendu :
1
Les commandes true et false sont particulièrement utiles pour apprendre la logique du shell :
trueretourne un succès ;falseretourne un échec.
Elles permettent d’observer les opérateurs sans dépendre d’un fichier, d’un réseau ou d’un service.
Attention : $? change après chaque commande
Le statut conservé dans $? correspond à la commande ou au pipeline exécuté juste avant.
Exemple :
false
echo "$?"
echo "$?"
Le premier echo affiche le statut de false.
Mais ce premier echo est lui-même une commande. Le second echo "$?" affiche donc le statut du premier echo, généralement 0.
Si vous avez besoin de conserver le statut pour plusieurs opérations, stockez-le immédiatement :
false
status=$?
printf 'Code capturé : %s\n' "$status"
Le principe est simple : capturez le statut avant d’exécuter une autre commande.
Préparer un lab sans risque
Ce lab utilise Bash et un répertoire temporaire.
Créons l’environnement :
LAB_DIR="$(mktemp -d)"
printf 'Lab : %s\n' "$LAB_DIR"
cd "$LAB_DIR"
Créons ensuite un fichier texte :
printf '%s\n' 'service web actif' 'sauvegarde terminée' 'espace disque contrôlé' > etat.txt
Vérification :
cat etat.txt
Nous allons uniquement lire ce fichier pendant les exemples.
Observer les codes de retour de grep
Cherchons un mot présent :
grep 'sauvegarde' etat.txt
echo "$?"
La ligne correspondante est affichée, puis le code attendu est :
0
Cherchons maintenant un mot absent :
grep 'erreur' etat.txt
echo "$?"
Aucune ligne n’est sélectionnée. Avec GNU grep, le code attendu est :
1
Enfin, provoquons une erreur contrôlée en utilisant un fichier inexistant :
grep 'test' fichier-inexistant.txt
echo "$?"
GNU grep affiche un message d’erreur et retourne normalement :
2
Nous obtenons donc trois situations différentes avec la même commande : correspondance, absence de correspondance et erreur réelle.
&& : continuer seulement après un succès
Une AND list Bash utilise &&.
Forme générale :
commande1 && commande2
commande2 n’est exécutée que si commande1 retourne 0.
Exemple :
grep -q 'sauvegarde' etat.txt &&
printf 'La sauvegarde est mentionnée.\n'
Le motif existe : grep réussit et printf est exécuté.
Essayons avec un motif absent :
grep -q 'erreur' etat.txt &&
printf 'Une erreur est mentionnée.\n'
Ici, grep retourne 1. La seconde commande n’est donc pas exécutée.
L’option -q demande à grep de rester silencieux. Dans ce cas, c’est principalement son statut qui nous intéresse.
Avec GNU grep, il existe toutefois une nuance importante : si -q trouve une correspondance, la commande peut retourner 0 même si une erreur survient aussi sur une autre entrée. Un statut 0 avec grep -q ne prouve donc pas, à lui seul, que toutes les entrées ont été traitées sans erreur.
|| : réagir à un échec
Une OR list Bash utilise ||.
Forme générale :
commande1 || commande2
commande2 n’est exécutée que si commande1 retourne un statut non nul.
Exemple :
grep -q 'erreur' etat.txt ||
printf 'Aucune ligne ne contient le mot erreur.\n'
Comme le motif est absent, le message est affiché.
Avec un motif présent :
grep -q 'sauvegarde' etat.txt ||
printf 'Sauvegarde introuvable.\n'
Le premier grep réussit. La commande suivant || n’est donc pas lancée.
; : exécuter la commande suivante quoi qu’il arrive
Le point-virgule n’applique pas de condition de succès ou d’échec.
Exemple :
false ; printf 'Cette commande est quand même exécutée.\n'
Même comportement avec :
true ; printf 'Cette commande est elle aussi exécutée.\n'
Dans les deux cas, la seconde commande est exécutée.
Cette différence est fondamentale :
commande1 && commande2 # commande2 seulement si commande1 réussit
commande1 || commande2 # commande2 seulement si commande1 échoue
commande1 ; commande2 # commande2 dans tous les cas
Enchaîner plusieurs décisions
Les opérateurs peuvent être combinés.
Exemple pédagogique :
grep -q 'sauvegarde' etat.txt &&
printf 'Contrôle OK\n' ||
printf 'Contrôle non validé\n'
Avec notre fichier, grep retourne 0, donc le premier printf est exécuté.
Il faut cependant rester prudent avec la lecture de cette forme.
Le || ne signifie pas strictement « sinon du grep ». Il fait partie d’une liste de commandes. Si le premier printf après && échouait lui-même, la partie située après || pourrait également être exécutée.
Pour une vraie structure conditionnelle comportant plusieurs traitements, un futur article utilisera if, then et else.
Pour des contrôles courts, && et || restent très pratiques.
Le statut d’une liste de commandes
Avec && et ||, Bash retourne le statut de la dernière commande effectivement exécutée dans la liste.
Exemple :
true && false
echo "$?"
Le premier true réussit, donc false est exécuté. Le statut final est alors non nul.
Inversement :
false && true
echo "$?"
true n’est jamais exécuté. Le statut de la liste correspond donc à celui de false.
Pour une liste séparée par ;, Bash exécute les commandes séquentiellement et le statut final est celui de la dernière commande exécutée.
Exemple :
false ; true
echo "$?"
Le résultat attendu est 0, parce que true est la dernière commande.
Ne pas confondre sortie visible et succès
Considérons :
grep 'erreur' etat.txt
La commande n’affiche rien, mais elle a tout de même produit une information exploitable : son code de retour vaut 1.
À l’inverse, une commande peut afficher un message puis échouer.
Le shell ne décide donc pas qu’une commande a réussi parce qu’elle « affiche quelque chose ». Il utilise son statut.
C’est précisément ce qui rend l’automatisation possible.
Bonnes pratiques
Consultez la documentation de la commande lorsque vous devez interpréter précisément ses codes non nuls.
Capturez $? immédiatement si vous devez réutiliser sa valeur plus tard :
commande
status=$?
Pour un enchaînement court, utilisez && lorsque la seconde opération dépend du succès de la première.
Utilisez || pour un traitement de repli simple ou un message lorsqu’une commande retourne un statut non nul.
N’utilisez pas ; lorsque la seconde commande ne doit s’exécuter qu’après le succès de la première.
Pour une logique comportant plusieurs branches ou plusieurs commandes par branche, préférez une structure if explicite plutôt qu’une chaîne difficile à lire.
Sécurité
Les codes de retour peuvent servir de garde avant une opération sensible.
Une construction comme :
verification && action
peut empêcher action de s’exécuter si verification échoue.
Mais cette protection n’est utile que si le programme de vérification fournit un statut adapté au besoin.
Il faut donc comprendre la sémantique de la commande testée avant d’utiliser son code de retour comme décision de sécurité ou d’exploitation.
Les exemples de cet article restent volontairement non destructifs.
Coûts
Le lab s’exécute localement et n’utilise aucun service cloud ou ressource facturable.
Limites et points de vigilance
Cet article ne couvre pas encore :
if,then,else;test,[ ]et[[ ]];- les options Bash comme
set -eouset -o pipefail; - le détail du statut des pipelines comportant plusieurs commandes ;
- les signaux et la manière dont ils influencent les statuts de terminaison ;
- les fonctions Bash et la commande
return.
Ces sujets nécessitent leur propre contexte. Ici, l’objectif reste de comprendre le mécanisme fondamental : une commande retourne un statut, et le shell peut s’en servir pour décider de la suite.
Rollback ou nettoyage
Le lab a créé uniquement un répertoire temporaire et un fichier texte.
Vérifiez d’abord le chemin :
printf '%s\n' "$LAB_DIR"
Puis quittez le répertoire et supprimez-le :
cd /
rm -rf -- "$LAB_DIR"
La suppression est volontairement limitée au chemin contenu dans LAB_DIR, créé au début du lab avec mktemp -d.
Références officielles
- Bash Reference Manual — Lists of Commands — GNU Project
- Bash Reference Manual — Special Parameters — GNU Project
- Bash Reference Manual — Bourne Shell Builtins — GNU Project
- GNU Grep Manual — Exit Status — GNU Project
Conclusion
Le code de retour est l’un des mécanismes les plus importants du shell.
Retenez trois règles :
0 = succès
non 0 = autre résultat ou échec selon la commande
$? = statut de la commande ou du pipeline précédent
À partir de là, &&, || et ; deviennent lisibles : ils ne sont pas de la ponctuation mystérieuse, mais des opérateurs qui contrôlent la suite des commandes à partir de leurs statuts.
