GNU/Linux >> Tutoriels Linux >  >> Linux

Augmenter l'erreur dans un script Bash

Il existe plusieurs façons d'aborder ce problème. En supposant que l'une de vos exigences est d'exécuter un script/une fonction shell contenant quelques commandes shell et de vérifier si le script s'est exécuté avec succès et de générer des erreurs en cas d'échec.

Les commandes shell reposent généralement sur les codes de sortie renvoyés pour indiquer au shell s'il a réussi ou échoué en raison d'événements inattendus.

Donc, ce que vous voulez faire relève de ces deux catégories

  • quitter en cas d'erreur
  • quitter et nettoyer en cas d'erreur

Selon celui que vous voulez faire, il existe des options de shell disponibles à utiliser. Pour le premier cas, le shell fournit une option avec set -e et pour la seconde vous pourriez faire un trap le EXIT

Dois-je utiliser exit dans mon script/fonction ?

Utilisation de exit améliore généralement la lisibilité Dans certaines routines, une fois que vous connaissez la réponse, vous souhaitez quitter immédiatement la routine d'appel. Si la routine est définie de telle sorte qu'elle ne nécessite aucun nettoyage supplémentaire une fois qu'elle détecte une erreur, ne pas quitter immédiatement signifie que vous devez écrire plus de code.

Donc, dans les cas où vous devez effectuer des actions de nettoyage sur le script pour rendre la fin du script propre, il est préférable de ne pas utiliser exit .

Dois-je utiliser set -e pour erreur à la sortie ?

Non !

set -e était une tentative d'ajouter une "détection d'erreur automatique" au shell. Son objectif était de provoquer l'abandon du shell chaque fois qu'une erreur se produisait, mais il comporte de nombreux pièges potentiels, par exemple,

  • Les commandes qui font partie d'un test if sont immunisées. Dans l'exemple, si vous vous attendez à ce qu'il se casse sur le test vérifier le répertoire inexistant, ce ne serait pas le cas, il passerait à la condition else

    set -e
    f() { test -d nosuchdir && echo no dir; }
    f
    echo survived
    
  • Les commandes dans un pipeline autre que le dernier sont immunisées. Dans l'exemple ci-dessous, parce que le code de sortie de la commande la plus récemment exécutée (la plus à droite) est pris en compte ( cat ) et c'est réussi. Cela pourrait être évité en définissant par le set -o pipefail option mais c'est toujours une mise en garde.

    set -e
    somecommand that fails | cat -
    echo survived 
    

Utilisation recommandée - trap à la sortie

Le verdict est si vous voulez être capable de gérer une erreur au lieu de sortir aveuglément, au lieu d'utiliser set -e , utilisez un trap sur le ERR pseudo-signal.

Le ERR le piège n'est pas d'exécuter du code lorsque le shell lui-même se termine avec un code d'erreur non nul, mais lorsqu'une commande exécutée par ce shell ne fait pas partie d'une condition (comme dans if cmd , ou cmd || ) sort avec un statut de sortie différent de zéro.

La pratique générale consiste à définir un gestionnaire d'interruptions pour fournir des informations de débogage supplémentaires sur la ligne et la cause de la sortie. Rappelez-vous le code de sortie de la dernière commande qui a causé le ERR le signal serait toujours disponible à ce stade.

cleanup() {
    exitcode=$?
    printf 'error condition hit\n' 1>&2
    printf 'exit code returned: %s\n' "$exitcode"
    printf 'the command executing at the time of the error was: %s\n' "$BASH_COMMAND"
    printf 'command present on line: %d' "${BASH_LINENO[0]}"
    # Some more clean up code can be added here before exiting
    exit $exitcode
}

et nous utilisons simplement ce gestionnaire comme ci-dessous en plus du script qui échoue

trap cleanup ERR

Rassembler cela sur un script simple contenant false à la ligne 15, les informations que vous obtiendriez comme

error condition hit
exit code returned: 1
the command executing at the time of the error was: false
command present on line: 15

Le trap fournit également des options indépendamment de l'erreur pour exécuter simplement le nettoyage à la fin du shell (par exemple, votre script shell se termine), sur le signal EXIT . Vous pouvez également piéger sur plusieurs signaux en même temps. La liste des signaux pris en charge sur lesquels piéger peut être trouvée sur la page de manuel trap.1p - Linux

Une autre chose à noter serait de comprendre qu'aucune des méthodes fournies ne fonctionne si vous avez affaire à des sous-shells, auquel cas vous devrez peut-être ajouter votre propre gestion des erreurs.

  • Sur un sous-shell avec set -e ne fonctionnerait pas. Le false est limité au sous-shell et n'est jamais propagé au shell parent. Pour faire la gestion des erreurs ici, ajoutez votre propre logique pour faire (false) || false

    set -e
    (false)
    echo survived
    
  • La même chose se produit avec trap aussi. La logique ci-dessous ne fonctionnerait pas pour les raisons mentionnées ci-dessus.

    trap 'echo error' ERR
    (false)
    

Gestion des erreurs de base

Si votre exécuteur de cas de test renvoie un code différent de zéro pour les tests ayant échoué, vous pouvez simplement écrire :

test_handler test_case_x; test_result=$?
if ((test_result != 0)); then
  printf '%s\n' "Test case x failed" >&2  # write error message to stderr
  exit 1                                  # or exit $test_result
fi

Ou encore plus court :

if ! test_handler test_case_x; then
  printf '%s\n' "Test case x failed" >&2
  exit 1
fi

Ou le plus court :

test_handler test_case_x || { printf '%s\n' "Test case x failed" >&2; exit 1; }

Pour quitter avec le code de sortie de test_handler :

test_handler test_case_x || { ec=$?; printf '%s\n' "Test case x failed" >&2; exit $ec; }

Gestion avancée des erreurs

Si vous souhaitez adopter une approche plus globale, vous pouvez avoir un gestionnaire d'erreur :

exit_if_error() {
  local exit_code=$1
  shift
  [[ $exit_code ]] &&               # do nothing if no error code passed
    ((exit_code != 0)) && {         # do nothing if error code is 0
      printf 'ERROR: %s\n' "[email protected]" >&2 # we can use better logging here
      exit "$exit_code"             # we could also check to make sure
                                    # error code is numeric when passed
    }
}

puis invoquez-le après avoir exécuté votre scénario de test :

run_test_case test_case_x
exit_if_error $? "Test case x failed"

ou

run_test_case test_case_x || exit_if_error $? "Test case x failed"

Les avantages d'avoir un gestionnaire d'erreurs comme exit_if_error sont :

  • nous pouvons normaliser toute la logique de gestion des erreurs, telle que la journalisation, l'impression d'une trace de pile, la notification, le nettoyage, etc., en un seul endroit
  • en faisant en sorte que le gestionnaire d'erreurs obtienne le code d'erreur comme argument, nous pouvons épargner à l'appelant l'encombrement de if blocs qui testent les codes de sortie pour les erreurs
  • si nous avons un gestionnaire de signal (utilisant trap), nous pouvons invoquer le gestionnaire d'erreurs à partir de là

Bibliothèque de gestion et de journalisation des erreurs

Voici une implémentation complète de la gestion et de la journalisation des erreurs :

https://github.com/codeforester/base/blob/master/lib/stdlib.sh

Articles associés

  • Gestion des erreurs dans Bash
  • La commande intégrée "appelant" sur le wiki de Bash Hackers
  • Existe-t-il des codes d'état de sortie standard sous Linux ?
  • BashFAQ/105 - Pourquoi set -e (ou set -o errexit, ou trap ERR) ne fait-il pas ce que j'attendais ?
  • Équivalent de __FILE__ , __LINE__ dans Bash
  • Existe-t-il une commande TRY CATCH dans Bash
  • Pour ajouter une trace de pile au gestionnaire d'erreurs, vous pouvez consulter cet article :Trace des programmes exécutés appelés par un script Bash
  • Ignorer des erreurs spécifiques dans un script shell
  • Récupérer les codes d'erreur dans un shell pipe
  • Comment gérer la verbosité des journaux dans un script shell ?
  • Comment consigner le nom de la fonction et le numéro de ligne dans Bash ?
  • Les crochets doubles [[ ]] sont-ils préférables aux crochets simples [ ] dans Bash ?

Cela dépend de l'endroit où vous souhaitez que le message d'erreur soit stocké.

Vous pouvez effectuer les actions suivantes :

echo "Error!" > logfile.log
exit 125

Ou ce qui suit :

echo "Error!" 1>&2
exit 64

Lorsque vous déclenchez une exception, vous arrêtez l'exécution du programme.

Vous pouvez également utiliser quelque chose comme exit xxxxxx est le code d'erreur que vous voudrez peut-être renvoyer au système d'exploitation (de 0 à 255). Ici 125 et 64 ne sont que des codes aléatoires avec lesquels vous pouvez sortir. Lorsque vous devez indiquer au système d'exploitation que le programme s'est arrêté anormalement (par exemple, une erreur s'est produite), vous devez transmettre un code de sortie non nul à exit .

Comme @chepner l'a souligné, vous pouvez faire exit 1 , ce qui signifie une erreur non spécifiée .


Linux
  1. Apprendre la gestion des erreurs Bash par exemple

  2. Un script Bash peut-il être accroché à un fichier ?

  3. Typeset -a donne une erreur dans le script ?

  4. Script Bash pour remplir un modèle ?

  5. Pourquoi le script Bash ne se ferme-t-il pas après l'exécution ?

Commande de sortie Bash et codes de sortie

35 exemples de scripts bash

Comment exécuter un script bash

Que signifie set -e dans un script bash ?

Correspondance de modèle de script bash

Exécutez la commande bash sur le pipeline jenkins