Pour qu'un pipeline zsh signale un échec survenu à n'importe quelle étape, activez l'option PIPE_FAIL avant de l'exécuter. Dans Terminal sur Mac, entrez setopt PIPE_FAIL ; dans un script, placez cette ligne avant le pipeline. Le pipeline renvoie alors le code de sortie de sa commande en échec la plus à droite, ou zéro si toutes les commandes réussissent. Capturez $? immédiatement après le pipeline si des commandes ultérieures ne doivent pas remplacer ce résultat. Le manuel des options de zsh documente ce comportement.
Pourquoi un pipeline en échec peut sembler réussir
Par défaut, zsh attribue à un pipeline le code de sortie de sa dernière commande. Ainsi, si une commande précédente échoue mais que la commande finale réussit, le pipeline global peut sembler avoir réussi. Par exemple, la commande finale cat ci-dessous peut réussir même lorsque grep ne trouve aucune ligne correspondante :
printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- $?
Le manuel de zsh définit un pipeline comme une suite de commandes reliées par | et précise que sa valeur est normalement celle de la dernière commande. Un statut de zéro indique un succès ; un statut différent de zéro signifie que la commande ou le pipeline a signalé un échec. La documentation des pipelines de zsh décrit la règle par défaut.
Activer PIPE_FAIL pour le pipeline
Dans une session Terminal, définissez l'option avant d'exécuter la commande :
setopt PIPE_FAIL
printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- $?
Le statut final est désormais non nul parce que grep a échoué, même si cat a réussi. PIPE_FAIL utilise le statut non nul le plus à droite dans le pipeline. Cela préserve la signification de l'échec tout en renvoyant le code de statut réel sélectionné par zsh. Pour désactiver à nouveau ce comportement dans la même session, exécutez unsetopt PIPE_FAIL.
Pour un script, placez l'option vers le début, après la ligne de l'interpréteur (shebang) et avant tout pipeline concerné :
#!/bin/zsh
setopt PIPE_FAIL
printf '%s\n' 'ready' | grep 'missing' | cat
pipeline_status=$?
if (( pipeline_status != 0 )); then
print -u2 -- "Pipeline failed with status $pipeline_status"
exit "$pipeline_status"
fi
L'affectation explicite du statut est importante : $? représente le statut de la dernière commande, de sorte qu'un print ultérieur, une commande liée à une variable ou une condition peuvent modifier ce qu'il rapporte. Sauvegarder la valeur immédiatement permet au script de la signaler ou de quitter avec celle-ci après d'autres opérations. Cet exemple renvoie un statut d'échec au programme qui a lancé le script ; il n'empêche pas les commandes individuelles du pipeline de s'exécuter dès qu'une étape précédente échoue.
Utiliser pipestatus quand vous avez besoin du résultat de chaque étape
PIPE_FAIL résume le pipeline. Si vous devez également identifier quelle commande a renvoyé quel code, zsh fournit le tableau spécial pipestatus, contenant le statut de chaque commande du dernier pipeline. Lisez-le immédiatement, car un autre pipeline remplacera ces valeurs. La référence des paramètres de zsh documente ce tableau.
printf '%s\n' 'ready' | grep 'missing' | cat
print -r -- "Stage statuses: ${pipestatus[*]}"
Pour cet exemple à trois commandes, la sortie affiche un zéro pour printf, une valeur non nulle pour grep et un zéro pour cat. Le code non nul exact dépend de la commande. Si vous avez besoin à la fois du résultat global et des résultats par étape, utilisez PIPE_FAIL et capturez immédiatement le $? global ; inspectez pipestatus immédiatement après l'exécution d'un pipeline distinct lors du diagnostic des étapes, ou concevez le script pour enregistrer d'abord le tableau et calculer l'agrégat souhaité à partir de ces données sauvegardées. Évitez d'insérer une autre commande ou un autre pipeline entre le pipeline et le statut que vous prévoyez de lire.
Conserver le paramètre dans le bon shell
Pour appliquer l'option aux commandes ultérieures dans une session interactive zsh, exécutez setopt PIPE_FAIL dans cette session. Pour l'activer pour chaque nouvelle session zsh, ajoutez la ligne à ~/.zshrc, puis ouvrez une nouvelle fenêtre de Terminal ou rechargez le fichier avec source ~/.zshrc. Le guide de Terminal d'Apple indique que zsh est le shell par défaut, tout en précisant que les paramètres ou les administrateurs peuvent choisir un shell différent. Confirmez le shell actif avant de vous fier à la syntaxe spécifique à zsh. Le guide de Terminal d'Apple explique comment Terminal choisit le shell.
Un script qui commence par #!/bin/zsh sollicite directement zsh, quel que soit le shell interactif configuré pour Terminal. Si le script est exécuté par un autre shell, setopt et pipestatus ne sont pas des substituts portables aux mécanismes de contrôle de pipeline propres à ce shell. Veillez à faire concorder l'interpréteur et la syntaxe, puis préservez le résultat du pipeline avant d'effectuer les tâches suivantes.
Sources
16 Options (zsh) — The Z Shell Manual
6 Shell Grammar (zsh) — The Z Shell Manual
15 Parameters (zsh) — The Z Shell Manual
Modifier le shell par défaut dans Terminal sur Mac — Assistance Apple