2008-12-15 5 views
28

Supposons qu'un script shell (/ bin/sh ou/bin/bash) contient plusieurs commandes. Comment puis-je nettoyer proprement le script si l'une des commandes a un statut de sortie défaillant? Évidemment, on peut utiliser des blocs et/ou des rappels, mais y a-t-il une manière plus propre et plus concise? L'utilisation de & & n'est pas vraiment une option non plus, car les commandes peuvent être longues, ou le script pourrait avoir des choses non triviales comme des boucles et des conditions.Scénarisation shell: mourir sur toute erreur

Répondre

52

à la norme sh et bash, vous pouvez

set -e 

Il sera

$ help set 
... 
     -e Exit immediately if a command exits with a non-zero status. 

Il fonctionne aussi (ce que je pouvais rassembler) avec zsh. Il devrait également fonctionner pour tout descendant de Bourne shell.

Avec csh/tcsh, vous devez lancer votre script avec #!/bin/csh -e

+0

Merci, cela semble être ce que je veux. Je devrais affûter mon Google fu, je suppose ... :) – Pistos

+3

Notez que les commandes dans les conditions peuvent échouer sans provoquer la sortie du script - ce qui est crucial. Par exemple: si grep quelque chose/un/où; alors: il a été trouvé; d'autre: il n'a pas été trouvé; ça marche bien, peu importe si quelque chose se trouve dans/un/où. –

+0

Vous dites "standard sh". Cela signifie-t-il que c'est POSIX? Edit: J'ai vérifié et c'est POSIX: http://pubs.opengroup.org/onlinepubs/009695399/utilities/set.html – Taywee

16

Peut être que vous pouvez utiliser:

$ <any_command> || exit 1 
+1

Ce modèle est utile pour ** quitter sélectivement en cas d'échec de commandes individuelles ** et aussi ** si vous voulez imprimer un message d'erreur personnalisé ** en premier. Pour ce faire, utilisez quelque chose comme ' || eval 'echo "Veuillez vous assurer ..." 1> & 2; exit 1'' NOTE: L'astuce 'eval' est nécessaire pour regrouper les commandes' echo' et 'exit' sans créer de _sub_-shell; l'utilisation d'un sous-shell (comme cela arriverait si vous utilisiez des parenthèses) rendrait inefficace 'exit'. – mklement0

+14

Vivre et apprendre: Bash a une fonction de regroupement de commandes, donc il n'y a pas besoin de l'astuce 'eval' que j'ai mentionnée. Au lieu de cela, utilisez ' || {echo "Veuillez vous assurer ..." 1> & 2; Sortie; } '(notez le'; 'requis avant la fermeture'} '). – mklement0

0

Vous pouvez vérifier $? pour voir ce que le code de sortie la plus récente est ..

par exemple

#!/bin/sh 
# A Tidier approach 

check_errs() 
{ 
    # Function. Parameter 1 is the return code 
    # Para. 2 is text to display on failure. 
    if [ "${1}" -ne "0" ]; then 
    echo "ERROR # ${1} : ${2}" 
    # as a bonus, make our script exit with the right error code. 
    exit ${1} 
    fi 
} 

### main script starts here ### 

grep "^${1}:" /etc/passwd > /dev/null 2>&1 
check_errs $? "User ${1} not found in /etc/passwd" 
USERNAME=`grep "^${1}:" /etc/passwd|cut -d":" -f1` 
check_errs $? "Cut returned an error" 
echo "USERNAME: $USERNAME" 
check_errs $? "echo returned an error - very strange!" 

+1

-1: Ceci est un antipattern trop fréquent. La solution de @ Barun est plus simple et plus idiomatique. – tripleee

+0

même script que ci-dessus, mais comme une "approche plus ordonnée" (tout sur une seule ligne): foo = $ (grep "^ $ {1}"/etc/passwd) && foo = $ (echo "$ foo" | -d: -f1) && echo ok || echo not_ok (nb: si vous utilisez bash, et non limité à sh, vous pouvez obtenir le statut de sortie de n'importe quelle partie d'un pipeline, ce qui simplifie la logique grep/cut.) – michael

+0

utilisez simplement set -e – Codefor

Questions connexes