All links of one day
in a single page.
<Previous day - Next day>

rss_feedDaily RSS Feed
floral_left The Daily Shaarli floral_right
——————————— Monday 18, February 2019 ———————————

Quand le gruik découvre set -e et trap ERR

Parfois, j'ai besoin d'écrire des scripts shell banals qui enchaînent des commandes qui dépendent les unes des autres (si l'une échoue, il ne faut pas exécuter les autres) et qui sont convenablement verbeuses pour ne pas justifier un traitement des erreurs. Un unique code d'erreur peut être retourné, quelle que soit la commande qui échoue. En termes d'algorithmique, ça se représente environ comme cela :

Si commande1 termine avec succès
{
    Si commande2 termine avec succès
    {
        Si commande3 termine avec succès
        {
            Sortir avec le code retour 0
        }
    }   
}

Afficher 'ERREUR ! SORTIE PRÉMATURÉE !'
Sortir avec le code retour 1



Une méthode qui vient assez intuitivement à l'esprit pour traiter cela est : commande1 && command2 && commande3 && exit 0 || exit 1. Cela fonctionne, mais c'est peu pratique quand on enchaîne beaucoup de commandes, que les commandes prennent des tas d'arguments voire des redirections de texte dans leur entrée standard, ou que l'on souhaite documenter un peu ce que fait notre enchaînement de commandes.

Bash propose deux commandes internes bien pratiques :

  • set -e, qui permet de terminer immédiatement le script dès qu'une commande (ou un ensemble de commandes, voir le manuel) échoue ;

  • trap <ma_commande> <signal>, qui permet d'exécuter une commande lorsqu'un signal est reçu (on dit « capturer un signal », d'où son nom ;) ).

set -e lève le signal ERR. Donc, il est possible de le capturer avec trap <ma_commande> ERR. Il devient donc possible d'afficher un message et de positionner un code d'erreur immédiatement lorsqu'une commande parmi un enchaînement échoue.



Appliquons cela à notre algorithme précédent :

#!/bin/bash
set -e
trap "echo 'ERREUR ! SORTIE PRÉMATURÉE !' && exit 1" ERR

commande1

commande2

commande3

exit 0



Sous ce format, il est parfaitement possible d'ajouter des commentaires voire des affichages (echo) pour des groupes de commandes qui le méritent (« Étape 1 : je fais ceci », « Étape 2 : je fais cela », etc.).

Si les commandes sont convenablement verbeuses, alors, en cas d'erreur, on lira « Étape 1 : je fais ceci » suivi de l'affichage de la commande suivi de « ERREUR ! SORTIE PRÉMATURÉE ! ». Difficile de ne pas comprendre qu'une erreur s'est produite dans le traitement de l'étape 1.

Je lis parfois cette mécanique utilisée avec trap EXIT. Je trouve cela moins pratique car un exit 0 déclenche tout autant la commande. Il est donc nécessaire d'arrêter de capturer le signal EXIT à la fin du script avec trap - EXIT, ce qui fait une commande de plus à mémoriser et à saisir.

Attention : comme le souligne ban, trap est inhibée par l'entrée dans une fonction. Le programme s'arrêtera immédiatement en cas d'erreur dans la fonction, mais l'action définie avec trap ne sera pas réalisée, sauf à re-définir trap à l'entrée de chaque fonction. On peut parfaitement écrire une fonction qui appelle trap puis appeler cette fonction au début du programme et de chacune de ses fonctions. Mais c'est du bricolage. Pour ma part, je confine mon utilisation de set -e + trap au cas d'usage présenté dans ce shaarli.

-