2009-05-04 20 views
306

Tengo un script de shell Bash que invoca una serie de comandos. Me gustaría que el script de shell salga automáticamente con un valor de retorno de 1 si alguno de los comandos devuelve un valor distinto de cero.Anulando un script de shell si algún comando devuelve un valor distinto de cero?

¿Esto es posible sin verificar explícitamente el resultado de cada comando?

p. Ej.

dosomething1 
if [[ $? -ne 0 ]]; then 
    exit 1 
fi 

dosomething2 
if [[ $? -ne 0 ]]; then 
    exit 1 
fi 
+5

Además de 'set -e', también' set -u' (o 'set -eu'). '-u' pone fin al comportamiento idiota y de ocultación de errores que puede acceder a cualquier variable inexistente y tiene un valor en blanco producido sin diagnósticos. – Kaz

Respuesta

550

Añadir esto al principio del script:

set -e 

Esto hará que la cáscara para salir de inmediato si un simple comando sale con un valor de salida distinto de cero. Un comando simple es cualquier comando que no forme parte de un if, while, o until test, o parte de un & & o || lista.

Consulte el bash(1) man page en el comando interno "set" para obtener más detalles.

Personalmente, comienzo casi todos los scripts de shell con "set -e". Es realmente molesto tener una secuencia de comandos obstinada cuando algo falla en el medio y rompe las suposiciones para el resto del guión.

+32

Eso funcionaría, pero me gusta usar "#!/Usr/bin/env bash" porque con frecuencia ejecuto bash desde otro lugar que no sea/bin. Y "#!/Usr/bin/env bash -e" no funciona. Además, es bueno tener un lugar para modificar para leer "set -xe" cuando quiero activar el seguimiento para la depuración. –

+2

Tenga en cuenta que esto puede no ser suficiente debido a las tuberías. Ver mi respuesta agregada. – leonbloy

+45

Además, los indicadores en la línea shebang se ignoran si un script se ejecuta como 'bash script.sh'. –

3

una expresión como

dosomething1 && dosomething2 && dosomething3 

se detendrá el procesamiento cuando uno de los comandos regresa con un valor distinto de cero. Por ejemplo, el siguiente comando nunca se imprimirá "hecho":

cat nosuchfile && echo "done" 
echo $? 
1 
12

Ejecutar con -e o set -e en la parte superior.

También vea set -u.

+26

Para potencialmente salvar a otros, la necesidad de leer a través de 'help set':' -u' trata las referencias a variables no configuradas como errores. – mklement0

+1

por lo que es 'set -u' o' set -e', no ambos? @lumpynose – ericn

+1

@eric Me retiré hace varios años. A pesar de que amaba mi trabajo, mi cerebro envejecido lo había olvidado todo. A tiempo, supongo que podrías usar ambos juntos; malas palabras de mi parte; Debería haber dicho "y/o". – lumpynose

64

Las declaraciones if en su ejemplo son innecesarias. Sólo hacerlo de esta manera:

dosomething1 || exit 1 

Si se toma el consejo de Ville Laurikari y utilizar set -e a continuación para algunos comandos puede que tenga que utilizar este:

dosomething || true 

El || true hará que la canalización de comandos tener un true devuelve valor incluso si el comando falla, por lo que la opción -e no matará el script.

+1

Me gusta esto. Especialmente porque la respuesta principal es bash-céntrica (para nada claro para mí si/en qué medida se aplica a las secuencias de comandos zsh). Y podría buscarlo, pero tu es más claro, porque la lógica. – g33kz0r

23

Si tiene que hacer una limpieza al salir, también puede usar 'trap' con la pseudo-señal ERR. Esto funciona de la misma manera que capturando INT o cualquier otra señal; fiesta de tiros ERR si hay algún comando sale con un valor distinto de cero:

# Create the trap with 
# trap COMMAND SIGNAME [SIGNAME2 SIGNAME3...] 
trap "rm -f /tmp/$MYTMPFILE; exit 1" ERR INT TERM 
command1 
command2 
command3 
# Partially turn off the trap. 
trap - ERR 
# Now a control-C will still cause cleanup, but 
# a nonzero exit code won't: 
ps aux | grep blahblahblah 

O, especialmente si se está usando "set -e", que podría atrapar salida; su trampa se ejecutará cuando el script se cierre por cualquier razón, incluyendo un final normal, interrupciones, una salida causada por la opción -e, etc.

8

La variable $? rara vez es necesaria. El pseudo-idioma command; if [ $? -eq 0 ]; then X; fi siempre debe escribirse como if command; then X; fi.

Los casos en los que se requiere $? es cuando se necesita ser comprobada contra múltiples valores:

command 
case $? in 
    (0) X;; 
    (1) Y;; 
    (2) Z;; 
esac 

o cuando $? necesidades para ser reutilizados o de otra manera manipular:

if command; then 
    echo "command successful" >&2 
else 
    ret=$? 
    echo "command failed with exit code $ret" >&2 
    exit $ret 
fi 
+1

¿Por qué "siempre debe escribirse como"? Quiero decir, ¿por qué "* debería *" ser así? Cuando un comando es largo (piense en invocar GCC con una docena de opciones), entonces es mucho más legible ejecutar el comando antes de verificar el estado de retorno. – ysap

+0

Si un comando es demasiado largo, puede dividirlo nombrándolo (definir una función de shell). –

138

a añadir a la respuesta aceptada:

Tenga en cuenta que set -e a veces no es suficiente, especialmente si tiene pip es.

Por ejemplo, suponga que tiene este script

#!/bin/bash 
set -e 
./configure > configure.log 
make 

... que funciona como se esperaba: un error en configure aborta la ejecución.

Mañana de hacer un cambio aparentemente trivial:

#!/bin/bash 
set -e 
./configure | tee configure.log 
make 

... y ahora no funciona. Esto se explica here, y se proporciona una solución alternativa (sólo Bash):

 
#!/bin/bash 
set -e 
set -o pipefail 

./configure | tee configure.log 
make 
-2

simplemente tirar en otro como referencia ya que no había una pregunta adicional a la entrada de la marca Edgars y aquí es un ejemplo adicional y toca en el tema general:

[[ `cmd` ]] && echo success_else_silence 

que es el mismo que cmd || exit errcode como alguien mostró.

por ejemplo. Quiero para asegurarse de que una partición se desmonta si se monta:

[[ `mount | grep /dev/sda1` ]] && umount /dev/sda1 
+3

No, '[[' cmd']] 'no es lo mismo. Es falso si la salida del comando está vacía y es verdadera, independientemente del estado de salida del comando. – Gilles

2
#!/bin/bash -e 

debería ser suficiente.

Cuestiones relacionadas