web-dev-qa-db-fra.com

Sortie automatique du script shell bash en cas d'erreur

J'ai écrit un script Shell et je le trouverais utile s'il était possible d'arrêter l'exécution dudit script Shell si l'une des commandes échouait. Voir ci-dessous pour un exemple:

#!/bin/bash  

cd some_dir  

./configure --some-flags  

make  

make install

Donc, dans ce cas, si le script ne peut pas changer dans le répertoire indiqué, il ne voudra certainement pas faire un ./configure par la suite s'il échoue.

Maintenant, je suis bien conscient que je pourrais avoir une vérification if pour chaque commande (ce qui, à mon avis, est une solution sans espoir), mais existe-t-il un paramètre global permettant de faire quitter le script si l'une des commandes échoue?

545
radman

Utilisez le set -e intégré:

_#!/bin/bash
set -e
# Any subsequent(*) commands which fail will cause the Shell script to exit immediately
_

Vous pouvez également passer _-e_ sur la ligne de commande:

_bash -e my_script.sh
_

Vous pouvez également désactiver ce comportement avec _set +e_.

Vous pouvez également vouloir utiliser tout ou partie des options _-e_ _-u_ _-x_ et _-o pipefail_ comme ceci:

_set -euxo pipefail
_

_-e_ ferme en cas d'erreur, _-u_ erreurs sur des variables non définies et -o (for option) pipefail se ferme en cas d'échec du canal de commande. Certains pièges et solutions de contournement sont bien documentés ici .

(*) Remarque:

Le shell fait pas quitter si la commande qui échoue fait partie de la liste de commandes suivant immédiatement un tant que ou jusqu'au mot-clé faisant partie du test suivant le si ou Elif mots réservés, faisant partie de toute commande exécutée dans un && ou || liste sauf la commande suivant la dernière && ou || , toute commande dans un pipeline sauf la dernière, ou si la valeur de retour de la commande est inversée avec !

(de _man bash_)

850
Adam Rosenfield

Pour quitter le script dès qu'une des commandes a échoué, ajoutez ceci au début:

set -e

Cela provoque la fermeture immédiate du script lorsqu'une commande ne faisant pas partie d'un test (comme dans une condition if [ ... ] ou une construction &&) se ferme avec un code de sortie différent de zéro.

63
sth

Voici comment faire:

#!/bin/sh

abort()
{
    echo >&2 '
***************
*** ABORTED ***
***************
'
    echo "An error occurred. Exiting..." >&2
    exit 1
}

trap 'abort' 0

set -e

# Add your script below....
# If an error occurs, the abort() function will be called.
#----------------------------------------------------------
# ===> Your script goes here
# Done!
trap : 0

echo >&2 '
************
*** DONE *** 
************
'
48
supercobra

Utilisez-le avec pipefail.

set -e
set -o pipefail

-e (errexit): Abandonne le script à la première erreur, lorsqu'une commande se termine avec un statut différent de zéro (sauf dans les boucles till ou while, if-tests, list)

-o pipefail: force un pipeline à renvoyer l'état de sortie de la dernière commande du canal qui a renvoyé une valeur de retour non nulle.

Chapitre 33. Options

34

Une alternative à la réponse acceptée qui s'inscrit dans la première ligne:

#!/bin/bash -e

cd some_dir  

./configure --some-flags  

make  

make install
22
Petr Peller

Un idiome est:

cd some_dir && ./configure --some-flags && make && make install

Je sais que cela peut être long, mais pour les scripts plus volumineux, vous pouvez le diviser en fonctions logiques.

19
Matthew Flaschen

Je pense que ce que vous recherchez est la commande trap:

trap command signal [signal ...]

Pour plus d'informations, voir cette page .

Une autre option consiste à utiliser la commande set -e en haut de votre script. Le script se fermera si un programme/une commande renvoie une valeur non vraie.

17
a_m0d

Un point oublié dans les réponses existantes est de montrer comment hériter des pièges d'erreur. Le shell bash fournit une telle option pour celle utilisant set

-E

Si défini, toute interruption sur ERR est héritée par les fonctions Shell, les substitutions de commandes et les commandes exécutées dans un environnement de sous-shell. Le piège ERR est normalement not ​​ hérité dans de tels cas.


réponse d'Adam Rosenfield la recommandation d'utiliser _set -e_ a raison dans certains cas, mais elle comporte ses propres pièges potentiels. Voir BashFAQ de GreyCat - 105 - Pourquoi set -e (ou set -o errexit, ou piège ERR) ne fait-il pas ce que j'attendais?

Selon le manuel, set -e exits

s'il s'agit d'une simple commande avec un statut différent de zéro. Le shell ne se ferme pas si la commande qui échoue fait partie de la liste de commandes suivant immédiatement un mot clé while ou until, qui fait partie du test in a if statement, élément d’une liste && ou || sauf le commande suivant le final && or ||, any command in a pipeline but the last ou si la valeur de retour de la commande est inversée via ! ".

ce qui signifie que _set -e_ ne fonctionne pas dans les cas simples suivants (des explications détaillées peuvent être trouvées sur le wiki)

  1. Utilisation de l'opérateur arithmétique let ou $((..)) (à partir de bash 4.1) pour incrémenter une valeur de variable

    _#!/usr/bin/env bash
    set -e
    i=0
    let i++                   # or ((i++)) on bash 4.1 or later
    echo "i is $i" 
    _
  2. Si la commande incriminée fait not ​​une partie de la dernière commande exécutée via _&&_ ou _||_. Par exemple le piège ci-dessous ne tirerait pas quand on s'attend à ce qu'il

    _#!/usr/bin/env bash
    set -e
    test -d nosuchdir && echo no dir
    echo survived
    _
  3. Lorsqu'il est utilisé de manière incorrecte dans une instruction if en tant que, le code de sortie de l'instruction if est le code de sortie de la dernière commande exécutée. Dans l'exemple ci-dessous, la dernière commande exécutée était echo qui ne déclencherait pas l'interruption, même si le _test -d_ a échoué.

    _#!/usr/bin/env bash
    set -e
    f() { if test -d nosuchdir; then echo no dir; fi; }
    f 
    echo survived
    _
  4. Lorsqu'elles sont utilisées avec une substitution de commande, elles sont ignorées, sauf si _inherit_errexit_ est défini avec bash 4.4.

    _#!/usr/bin/env bash
    set -e
    foo=$(expr 1-1; true)
    echo survived
    _
  5. lorsque vous utilisez des commandes qui ressemblent à des affectations mais ne le sont pas, telles que export, declare, typeset ou local. Ici, l'appel de la fonction à f va not ​​quitter car local a balayé le code d'erreur défini précédemment.

    _set -e
    f() { local var=$(somecommand that fails); }        
    g() { local var; var=$(somecommand that fails); }
    _
  6. Lorsqu'il est utilisé dans un pipeline, et que la commande incriminée est pas fait partie de la dernière commande. Par exemple la commande ci-dessous passerait toujours. Une option consiste à activer pipefail en renvoyant le code de sortie du processus premier ayant échoué:

    _set -e
    somecommand that fails | cat -
    echo survived
    _

La recommandation idéale est de not ​​utiliser _set -e_ et de mettre en place une propre version de la vérification des erreurs. Plus d'informations sur l'implémentation du traitement d'erreur personnalisé sur l'une de mes réponses à Erreur de levée dans un script Bash

0
Inian