2008-12-15 7 views
28

Suponga que un script de shell (/ bin/sh o/bin/bash) contiene varios comandos. ¿Cómo puedo hacer que la secuencia de comandos finalice limpiamente si alguno de los comandos tiene un estado de salida fallido? Obviamente, uno puede usar bloques y/o callbacks, pero ¿hay una manera más clara y concisa? Usar & & tampoco es una opción, porque los comandos pueden ser largos, o el script podría tener elementos no triviales como bucles y condicionales.Creación de scripts en el shell: se ha producido un error

Respuesta

52

Con el estándar sh y bash, puede

set -e 

Será

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

También funciona (por lo que pude reunir) con zsh. También debería funcionar para cualquier descendiente de shell Bourne.

Con csh/tcsh, usted tiene que poner en marcha su guión con #!/bin/csh -e

+0

Gracias, esto parece ser lo que yo quiero. Debo mejorar mi Google fu, supongo ... :) – Pistos

+3

Tenga en cuenta que los comandos en condicionales pueden fallar sin hacer que el script salga, lo cual es crucial. Por ejemplo: si grep algo/some/where; entonces: fue encontrado; else: no fue encontrado; fi funciona bien, independientemente de si se encuentra algo en/some/where. –

+0

Usted dice "sh estándar". ¿Esto significa que es POSIX? Editar: lo busqué y es POSIX: http://pubs.opengroup.org/onlinepubs/009695399/utilities/set.html – Taywee

16

Puede ser que usted podría usar:

$ <any_command> || exit 1 
+1

Este patrón es útil para ** salir selectivamente en caso de falla de comandos individuales ** y también ** si desea imprimir un mensaje de error personalizado ** primero. Para hacer esto último, use algo como ' || eval 'echo "Asegúrese de ..." 1> & 2; exit 1'' NOTA: El truco 'eval' es necesario para agrupar los comandos' echo' y 'exit' juntos sin crear un _sub_-shell; el uso de un subconjunto (como ocurriría si usaras paréntesis) dejaría la 'salida' ineficaz. – mklement0

+14

Vive y aprende: Bash tiene una función de agrupación por comando, por lo que no hay necesidad del truco 'eval' que mencioné. En su lugar, use ' || {echo "Por favor asegurate ..." 1> & 2; salida; } '(tenga en cuenta el'; 'requerido antes del cierre'} '). – mklement0

0

Puede comprobar $? para ver lo que el código de salida más reciente es ..

por ejemplo

#!/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: Este es un antipatrón muy frecuente. La solución de @Barun es más simple y más idiomática. – tripleee

+0

mismo script que el anterior, pero como un "enfoque más ordenado" más ordenado (todo en una línea): foo = $ (grep "^ $ {1}"/etc/passwd) && foo = $ (echo "$ foo" | cut -d: -f1) && echo ok || echo not_ok (nb: si usa bash, y no se limita a sh, uno puede obtener el estado de salida de cualquier parte de una tubería, lo que simplifica la lógica grep/cut.) – michael

+0

solo use el conjunto -e – Codefor

Cuestiones relacionadas