Skip to content

🇬🇧 English version of this article

La gestion des erreurs en Bash

En Bash, il est courant de voir des scripts avec peu ou pas de gestion des erreurs. Il existe de nombreuses approches différentes, mais aucune n'est vraiment parfaite.

L'une des méthodes consiste à créer une fonction check_error, appelée après chaque commande pour vérifier si le code de retour est bien égal à 0, un peu comme la gestion des erreurs en Golang. Toutefois, cette approche peut poser des problèmes, car même de simples avertissements (warnings) peuvent interrompre l'exécution du script.

Une autre technique consiste à utiliser les doubles pipes (||) pour gérer les erreurs dans certains cas. Mais, personnellement, je trouve que cela alourdit considérablement la lisibilité du script. En effet, à chaque commande, vous vous retrouvez avec :

commande_bash || echo "Error" ...

Certains vont même jusqu'à implémenter un bout de code pour vérifier les résultats attendus, un peu comme dans une approche TDD (Test-Driven Development). Pour moi, cela relève du mythe, un peu comme les licornes : tout le monde sait ce que c'est, mais personne n'en a jamais vraiment vu.

Après avoir testé plusieurs méthodes, ma préférence va désormais à la création d'une fonction handle_error qui est déclenchée grâce au mot-clé trap. Ce dernier permet d'exécuter une fonction en cas d'erreur ( ou d'exit ce qui est très pratique pour faire du nettoyage dans le répertoire de travail, par exemple).

L'élément clé de cette technique repose sur la ligne suivante :

trap 'handle_error ${LINENO} "${BASH_LINENO[*]}" "${BASH_SOURCE[*]}" "${BASH_COMMAND}" "${FUNCNAME[*]:-vide}"' ERR

Cependant, cette approche ne stoppe pas forcément l'exécution dans certains cas de commandes imbriquées. Pour cela, j'utilise également :

set -e

Malheureusement, cela ne suffit pas toujours. Je suis un adepte des fonctions imbriquées, et sans précautions supplémentaires, les erreurs survenant dans une sous-fonction (subfunction1), elle-même appelée par une autre fonction (function1), ne seront pas interceptées.

C'est ici que la commande suivante entre en jeu :

set -o errtrace

Elle permet à la commande trap d'attraper les erreurs même lorsqu'elles surviennent dans des fonctions appelées de manière imbriquée.

Mon template n'est sans doute pas parfait, et je suis preneur de vos retours. En attendant, n'hésitez pas à vous en inspirer pour créer une base de script Bash avec une gestion des erreurs plus robuste.

Exemple

#!/bin/bash

set -e
set -o errtrace
trap 'handle_error ${LINENO} "${BASH_LINENO[*]}" "${BASH_SOURCE[*]}" "${BASH_COMMAND}" "${FUNCNAME[*]:-empty}"' ERR

function handle_error() {
    local last_exit_code=$?
    local line_number=$1
    local bash_lineno=$2
    local bash_source=$3
    local bash_command=$4
    local func_name=${5:-empty}

    if [ $last_exit_code -ne 0 ]; then
        echo "---------------------------"
        echo "An error occurred. Exiting."
        echo "- Function: $func_name"
        echo "- Line: $line_number"
        echo "- File: $bash_source"
        echo "- Command: $bash_command"
        echo "- Exit Code: $last_exit_code"

        # Log the error to a file
        echo "$(date) - Error in function $func_name at line $line_number in file $bash_source: command '$bash_command' exited with status $last_exit_code" >> error.log

        exit $last_exit_code

    fi
}
function subfunction1(){
    ls -lht missingFileForCreateAError_sub_f1
}

function function1(){
    subfunction1
}



main(){
    function1
}
main $@

Commentez cet article de blog en utilisant un compte compatible Fediverse (Mastodon ou similaire).