Bash
Pourquoi ma CI réussit alors qu’une commande a échoué ?
Repérez les échecs masqués dans un pipeline ou par grep puis choisissez une réaction explicite au lieu de faire confiance à un statut trompeur.
Une CI peut afficher « réussi » alors qu’une commande a échoué. Le statut du pipeline peut masquer l’erreur en amont. Le script continue alors avec un résultat trompeur.
Pour diagnostiquer ce faux succès, vérifiez le statut du pipeline et les contextes où set -e ne quitte pas le shell. Distinguez aussi le code de grep qui signifie « absent » de celui qui signale une erreur. Vous pourrez ensuite choisir explicitement la réaction.
Commencer par le statut agrégé
Par défaut, Bash donne au pipeline le statut de sa dernière commande. Ainsi, false échoue mais true réussit :
false | true
Sans pipefail, le pipeline renvoie donc 0. Avec cette option, il renvoie le statut non nul de la commande échouée la plus à droite :
bash -c 'false | true; printf "default=%s\n" "$?"'
bash -o pipefail -c 'false | true; printf "pipefail=%s\n" "$?"'
Dans l’essai avec GNU Bash 5.3.9, les sorties sont default=0 et pipefail=1. Cette option rend l’échec visible dans le statut du pipeline. Elle ne coupe pas les autres commandes dès qu’une commande échoue. Le script doit encore décider comment traiter cet échec.
Ce que set -e ne signifie pas
set -e ne transforme pas chaque statut non nul en arrêt inconditionnel. Le contexte compte : une commande utilisée comme condition de if, dans une liste && ou ||, après ! ou dans certaines positions d’un pipeline peut ne pas arrêter le shell.
bash -e -c 'if false; then :; fi; printf "conditional=continued\n"'
Avec GNU Bash 5.3.9, cet exemple affiche conditional=continued et termine avec le code 0. Un false employé comme condition est attendu par le if; il ne fait pas quitter le shell. Les fonctions, substitutions de commande, sous-shells et options comme inherit_errexit ajoutent des cas particuliers. Testez la structure réelle du script avec son shell et ses options.
Traiter les trois issues de grep
Une recherche sans résultat n’est pas forcément une erreur. GNU grep renvoie 0 si une ligne correspond, 1 si aucune ne correspond et 2 en cas d’erreur. Évitez -q lorsque vous devez aussi détecter une erreur de lecture : GNU grep peut renvoyer 0 dès qu’il trouve une correspondance, même après avoir signalé un fichier illisible. Redirigez la sortie vers /dev/null et traitez les statuts séparément :
if grep -- "$pattern" "$file" >/dev/null; then
printf 'correspondance trouvée\n'
else
grep_status=$?
case "$grep_status" in
1) printf 'aucune correspondance\n' ;;
*) printf 'grep a échoué (code %s)\n' "$grep_status" >&2
exit "$grep_status" ;;
esac
fi
Le if permet de traiter le statut sans laisser set -e confondre « aucune correspondance » et une erreur. Le script accepte le code 1 comme absence puis interrompt le traitement pour les autres statuts non nuls. Pour une autre implémentation de grep, vérifiez les codes documentés : ils peuvent différer.
| Situation | Statut GNU grep 3.12 observé | Interprétation |
|---|---|---|
| Motif présent dans un fichier temporaire lisible | 0 | Correspondance |
| Motif absent d’un fichier temporaire lisible | 1 | Pas de correspondance |
| Chemin de fichier inexistant | 2 | Erreur d’ouverture/lecture |
GNU grep -q, fichier manquant puis fichier avec correspondance |
0 malgré le diagnostic du fichier manquant | Le mode -q masque l’erreur dès qu’une ligne correspond |
Ces cas ont été reproduits avec GNU grep 3.12 dans des fichiers temporaires. Le cas -q renvoie bien 0 après une correspondance malgré le diagnostic du fichier manquant. Cette limite rend le choix de l’option important quand l’intégrité de la lecture compte.
Corriger le faux succès sans masquer le vrai résultat
Quand une CI passe malgré une erreur, commencez par réduire le cas à la commande ou au pipeline fautif. Vérifiez si pipefail est actif et quel statut Bash renvoie. Repérez ensuite si set -e rencontre l’échec dans un contexte conditionnel. Enfin, si grep intervient, distinguez explicitement succès, absence et erreur. Faites échouer le job pour une erreur réelle; traitez une absence comme un cas métier seulement si c’est bien ce que le script attend.
Les essais du 28 septembre 2026 ont été exécutés avec GNU Bash 5.3.9 et GNU grep 3.12.
Sources techniques
- Bash Reference Manual — PipelinesGNU Project
- GNU Grep 3.12 — Exit StatusGNU Project