2008-09-18 13 views
316

Tengo un script de shell que ejecuta una serie de comandos. ¿Cómo puedo hacer que el script de shell salga si alguno de los comandos sale con un código de salida distinto de cero?Salir del script de shell basado en el código de salida del proceso

+2

me respondió asumiendo que usted está utilizando bash, pero si es una cáscara muy diferente que puede especificar en su puesto? –

Respuesta

402

Después de cada comando, el código de salida puede ser encontrado en la variable $? por lo que tendría algo como:

ls -al file.ext 
rc=$?; if [[ $rc != 0 ]]; then exit $rc; fi 

Necesita ser cuidadoso de comandos por tubería desde la $? sólo le da el código de retorno del último elemento de la tubería así, en el código:

ls -al file.ext | sed 's/^/xx: /" 

no devolverá un código de error si el archivo doesn' t existe (ya que la parte sed de la tubería realmente funciona, devolviendo 0).

El shell bash realmente proporciona una matriz que puede ayudar en ese caso, que es PIPESTATUS. Esta matriz tiene un elemento para cada uno de los componentes de canalización, que se puede acceder de forma individual como ${PIPESTATUS[0]}:

pax> false | true ; echo ${PIPESTATUS[0]} 
1 

Tenga en cuenta que esto es conseguir que el resultado del comando false, no toda la tubería. También puede obtener toda la lista para procesar como mejor le parezca:

pax> false | true | false; echo ${PIPESTATUS[*]} 
1 0 1 

Si quisiera obtener el código de error más grande de una tubería, podría utilizar algo como:

true | true | false | true | false 
rcs=${PIPESTATUS[*]}; rc=0; for i in ${rcs}; do rc=$(($i > $rc ? $i : $rc)); done 
echo $rc 

Esto pasa por cada uno de los elementos PIPESTATUS a su vez, almacenándolo en rc si fue mayor que el valor anterior rc.

+37

La misma característica en una sola línea de código portátil: 'ls -al file.ext || exit $? '([[]] no es portable) – MarcH

+18

MarcH, creo que encontrará que' [[]] 'es bastante portable en' bash', que es lo que la pregunta está etiquetada :-) Curiosamente, 'ls' no funciona en' command.com', por lo que tampoco es portátil, aunque lo sé, es el mismo tipo de argumento que presentas. – paxdiablo

+35

Sé que esto es antiguo, pero debe tenerse en cuenta que puede obtener el código de salida de comandos en una tubería a través de la matriz 'PIPESTATUS' (es decir,' $ {PIPESTATUS [0]} 'para el primer comando,' $ { PIPESTATUS [1]} 'para el segundo, o' $ {PIPESTATUS [*]} 'para obtener una lista de todas las estaciones de salida. – DevSolar

8

En bash esto es fácil, simplemente atarlas con & &:

command1 && command2 && command3 

También puede utilizar el anidado construcción if:

if command1 
    then 
     if command2 
      then 
       do_something 
      else 
       exit 
     fi 
    else 
     exit 
fi 
44

"set -e" es probablemente la forma más fácil de hacerlo. Solo ponlo antes de cualquier comando en tu programa.

+22

¿Qué hace? Cualquier enlace a documentos por favor? –

+5

@SwaroopCH 'set -e' su script se cancelará si algún comando en su script sale con estado de error y no ha manejado este error. – Andrew

195

Si quieres trabajar con $ ?, deberás comprobarlo después de cada comando, ya que $? se actualiza después de que sale cada comando. Esto significa que si ejecuta una tubería, solo obtendrá el código de salida del último proceso en la tubería.

Otro enfoque es hacer esto:

set -e 
set -o pipefail 

Si pones esto en la parte superior de la secuencia de comandos shell, se ve como fiesta se hará cargo de esto para usted. Como se señaló en un póster anterior, "set -e" hará que bash salga con un error en cualquier comando simple. "set -o pipefail" hará que bash salga con un error en cualquier comando en una tubería también.

Consulte here o here para obtener un poco más de discusión sobre este problema. Here es la sección manual de bash en el conjunto incorporado.

+4

Esto realmente debería ser la respuesta principal: es mucho, mucho más fácil hacer esto que usar 'PIPESTATUS' y verificar los códigos de salida en todas partes. – candu

+0

'#!/Bin/bash -e' es la única forma de iniciar un script de shell. Siempre puedes usar cosas como 'foo || handle_error $? 'si necesita examinar los estados de salida. –

24

Si solo llama a exit en el bash sin parámetros, devolverá el código de salida del último comando. Combinado con O, bash solo debería invocar exit, si el comando anterior falla. Pero no he probado esto.

 
command1 || exit; 
command2 || exit; 

The Bash también almacenará el código de salida del último comando en la variable $ ?.

17

de fiesta:

# this will trap any errors or commands with non-zero exit status 
# by calling function catch_errors() 
trap catch_errors ERR; 

# 
# ... the rest of the script goes here 
# 

function catch_errors() { 
    # do whatever on errors 
    # 
    # 
    echo "script aborted, because of errors"; 
    exit 0; 
} 
+16

Probablemente no debería "salir 0", ya que eso indica éxito. – nobar

+3

exit_code = $ ?; echo "script abortado, debido a errores"; exit $ exit_code – RaSergiy

+1

@ HAL9001 ¿Tiene evidencia de esto? Documentación de IBM [dice lo contrario] (http://www.ibm.com/developerworks/aix/library/au-usingtraps/#list4). –

20

http://cfaj.freeshell.org/shell/cus-faq-2.html#11

  1. ¿Cómo consigo el código de salida de cmd1 en cmd1|cmd2

    En primer lugar, tenga en cuenta que cmd1 salida el código podría ser distinto de cero y todavía no significa significa un error. Esto sucede por ejemplo en

    cmd | head -1 
    

    es posible observar un código de salida de 141 (o 269 con ksh93) de cmd1, pero es porque cmd fue interrumpida por una señal SIGPIPE cuando head -1 terminado después de haber leído una sola línea.

    Conocer el estado de salida de los elementos de una tubería cmd1 | cmd2 | cmd3

    a. con zsh:

    Los códigos de salida se proporcionan en la matriz especial de pipestatus. código cmd1 salida está en $pipestatus[1], cmd3 código de salida en $pipestatus[3], de manera que $? es siempre el mismo que $pipestatus[-1].

    b. con bash:

    Los códigos de salida se proporcionan en la matriz especial PIPESTATUS. código cmd1 salida está en ${PIPESTATUS[0]}, cmd3 código de salida en ${PIPESTATUS[2]}, de manera que $? es siempre el mismo que ${PIPESTATUS: -1}.

    ...

    Para más detalles ver la siguiente link.

18
[ $? -eq 0 ] || exit $?; # exit for none-zero return code 
+2

¿No debería ser '' exit $? '' (No parens)? – malthe

4
# 
#------------------------------------------------------------------------------ 
# run a command on failure exit with message 
# doPrintHelp: doRunCmdOrExit "$cmd" 
# call by: 
# set -e ; doRunCmdOrExit "$cmd" ; set +e 
#------------------------------------------------------------------------------ 
doRunCmdOrExit(){ 
    cmd="[email protected]" ; 

    doLog "DEBUG running cmd or exit: \"$cmd\"" 
    msg=$($cmd 2>&1) 
    export exit_code=$? 

    # if occured during the execution exit with error 
    error_msg="Failed to run the command: 
     \"$cmd\" with the output: 
     \"$msg\" !!!" 

    if [ $exit_code -ne 0 ] ; then 
     doLog "ERROR $msg" 
     doLog "FATAL $msg" 
     doExit "$exit_code" "$error_msg" 
    else 
     #if no errors occured just log the message 
     doLog "DEBUG : cmdoutput : \"$msg\"" 
     doLog "INFO $msg" 
    fi 

} 
#eof func doRunCmdOrExit 
+1

Raramente hay una razón para usar '$ *'; use '" $ @ "' en su lugar para conservar espacios y comodines. –

Cuestiones relacionadas