Salir de la secuencia de comandos basada en el código de salida del proceso
378
Tengo un script de shell que ejecuta una serie de comandos. ¿Cómo hago que salga el script de shell si alguno de los comandos sale con un código de salida distinto de cero?
Respondí asumiendo que estás usando bash, pero si es un shell muy diferente, ¿puedes especificarlo en tu publicación?
Martin W
Método difícil: prueba el valor de $?después de cada comando. Método fácil: poner set -eo #!/bin/bash -een la parte superior de su script Bash.
mwfearnley
Respuestas:
495
Después de cada comando, el código de salida se puede encontrar en la $?variable para que tenga algo como:
ls -al file.ext
rc=$?;if[[ $rc !=0]];then exit $rc;fi
Debe tener cuidado con los comandos canalizados, ya que $?solo le da el código de retorno del último elemento en la tubería, por lo tanto, en el código:
ls -al file.ext | sed 's/^/xx: /"
no devolverá un código de error si el archivo no existe (ya que la sedparte de la tubería realmente funciona, devolviendo 0).
El bashshell realmente proporciona una matriz que puede ayudar en ese caso, ese ser PIPESTATUS. Esta matriz tiene un elemento para cada uno de los componentes de la tubería, al que puede acceder individualmente como ${PIPESTATUS[0]}:
pax> false | true ; echo ${PIPESTATUS[0]}1
Tenga en cuenta que esto le está dando el resultado del falsecomando, no toda la tubería. También puede obtener la lista completa para procesar como mejor le parezca:
Misma función en solo una línea de código portátil: ls -al file.ext || exit $?([[]] no es portátil)
MarcH
19
MarcH, creo que encontrarás que [[ ]]es bastante portátil bash, que es en lo que está etiquetada la pregunta :-) Curiosamente, lsno funciona, command.comasí que tampoco es portátil, lo sé, pero es el mismo tipo de argumento que tú. presente.
paxdiablo
39
Sé que esto es antiguo, pero debe tenerse en cuenta que puede obtener el código de salida de los 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 todos los estados de salida.
DevSolar
11
Es necesario enfatizar que las secuencias de comandos de shell elegantes e idiomáticas rara vez necesitan examinarse $?directamente. Por lo general, desea algo como lo if ls -al file.ext; then : nothing; else exit $?; fique, por supuesto, como @MarcH dice que es equivalente, ls -al file.ext || exit $?pero si las cláusulas theno elseson algo más complejas, es más fácil de mantener.
tripleee el
99
[[ $rc != 0 ]]le dará un error 0: not foundo 1: not foundEsto debería cambiarse a [ $rc -ne 0 ]. También rc=$?podría ser eliminado y simplemente utilizado [ $? -ne 0 ].
CurtisLeeBolin
223
Si desea trabajar con $ ?, deberá verificarlo después de cada comando, ya que $? se actualiza después de cada comando sale. 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-eset-o pipefail
Si coloca esto en la parte superior del script de shell, parece que bash se encargará de esto por usted. Como señaló 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.
Vea aquí o aquí para un poco más de discusión sobre este problema. Aquí está la sección del manual de bash en el conjunto incorporado.
Esta realmente debería ser la respuesta principal: es mucho, mucho más fácil hacer esto que usar PIPESTATUSy verificar los códigos de salida en todas partes.
candu
2
#!/bin/bash -ees la única forma de iniciar un script de shell. Siempre puede usar cosas como foo || handle_error $?si realmente necesita examinar los estados de salida.
Davis Herring
53
" set -e" es probablemente la forma más fácil de hacer esto. Simplemente ponga eso antes de cualquier comando en su programa.
@SwaroopCH set -esu secuencia de comandos se cancelará si algún comando en su secuencia de comandos sale con estado de error y usted no manejó este error.
Andrew
2
set -ees 100% equivalente al set -o errexitque a diferencia del primero se puede buscar. Busque opengroup + errexit para obtener documentación oficial.
MarcH
30
Si solo llama a exit en el bash sin parámetros, devolverá el código de salida del último comando. Combinado con OR, el bash solo debería invocar la salida, si falla el comando anterior. Pero no he probado esto.
comando1 || salida;
comando2 || salida;
Bash también almacenará el código de salida del último comando en la variable $ ?.
¿Cómo obtengo el código de salida de cmd1encmd1|cmd2
Primero, tenga en cuenta que el cmd1código de salida podría ser distinto de cero y aún así no significa un error. Esto sucede por ejemplo en
cmd | head -1
puede observar un estado de salida 141 (o 269 con ksh93) cmd1, pero es porque cmdfue interrumpido por una señal SIGPIPE cuando
head -1terminó después de haber leído una 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 pipestatus.
cmd1el código de salida está en $pipestatus[1], el cmd3código de salida en
$pipestatus[3], de modo que $?siempre es el mismo que
$pipestatus[-1].
si. con golpe:
Los códigos de salida se proporcionan en la PIPESTATUSmatriz especial.
cmd1el código de salida está en ${PIPESTATUS[0]}, el cmd3código de salida en
${PIPESTATUS[2]}, de modo que $?siempre es el mismo que
${PIPESTATUS: -1}.
# 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;}
+1 Esta fue la solución más simple que estaba buscando. Además, también puede escribir if (! command)si espera un código de error distinto de cero del comando.
Berci
esto es para comandos secuenciales ... ¿y si quiero lanzar esos 3 en paralelo y matar a todos si alguno falla?
Vasile Surdu
4
##------------------------------------------------------------------------------# run a command on failure exit with message# doPrintHelp: doRunCmdOrExit "$cmd"# call by:# set -e ; doRunCmdOrExit "$cmd" ; set +e#------------------------------------------------------------------------------
doRunCmdOrExit(){
cmd="$@";
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
$?
después de cada comando. Método fácil: ponerset -e
o#!/bin/bash -e
en la parte superior de su script Bash.Respuestas:
Después de cada comando, el código de salida se puede encontrar en la
$?
variable para que tenga algo como:Debe tener cuidado con los comandos canalizados, ya que
$?
solo le da el código de retorno del último elemento en la tubería, por lo tanto, en el código:no devolverá un código de error si el archivo no existe (ya que la
sed
parte de la tubería realmente funciona, devolviendo 0).El
bash
shell realmente proporciona una matriz que puede ayudar en ese caso, ese serPIPESTATUS
. Esta matriz tiene un elemento para cada uno de los componentes de la tubería, al que puede acceder individualmente como${PIPESTATUS[0]}
:Tenga en cuenta que esto le está dando el resultado del
false
comando, no toda la tubería. También puede obtener la lista completa para procesar como mejor le parezca:Si desea obtener el código de error más grande de una tubería, puede usar algo como:
Esto pasa a través de cada uno de los
PIPESTATUS
elementos a su vez, almacenándolosrc
si fue mayor que elrc
valor anterior .fuente
ls -al file.ext || exit $?
([[]] no es portátil)[[ ]]
es bastante portátilbash
, que es en lo que está etiquetada la pregunta :-) Curiosamente,ls
no funciona,command.com
así que tampoco es portátil, lo sé, pero es el mismo tipo de argumento que tú. presente.PIPESTATUS
(es decir,${PIPESTATUS[0]}
para el primer comando,${PIPESTATUS[1]}
para el segundo o${PIPESTATUS[*]}
para obtener una lista de todos los estados de salida.$?
directamente. Por lo general, desea algo como loif ls -al file.ext; then : nothing; else exit $?; fi
que, por supuesto, como @MarcH dice que es equivalente,ls -al file.ext || exit $?
pero si las cláusulasthen
oelse
son algo más complejas, es más fácil de mantener.[[ $rc != 0 ]]
le dará un error0: not found
o1: not found
Esto debería cambiarse a[ $rc -ne 0 ]
. Tambiénrc=$?
podría ser eliminado y simplemente utilizado[ $? -ne 0 ]
.Si desea trabajar con $ ?, deberá verificarlo después de cada comando, ya que $? se actualiza después de cada comando sale. 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:
Si coloca esto en la parte superior del script de shell, parece que bash se encargará de esto por usted. Como señaló 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.
Vea aquí o aquí para un poco más de discusión sobre este problema. Aquí está la sección del manual de bash en el conjunto incorporado.
fuente
PIPESTATUS
y verificar los códigos de salida en todas partes.#!/bin/bash -e
es la única forma de iniciar un script de shell. Siempre puede usar cosas comofoo || handle_error $?
si realmente necesita examinar los estados de salida."
set -e
" es probablemente la forma más fácil de hacer esto. Simplemente ponga eso antes de cualquier comando en su programa.fuente
set -e
su secuencia de comandos se cancelará si algún comando en su secuencia de comandos sale con estado de error y usted no manejó este error.set -e
es 100% equivalente alset -o errexit
que a diferencia del primero se puede buscar. Busque opengroup + errexit para obtener documentación oficial.Si solo llama a exit en el bash sin parámetros, devolverá el código de salida del último comando. Combinado con OR, el bash solo debería invocar la salida, si falla el comando anterior. Pero no he probado esto.
Bash también almacenará el código de salida del último comando en la variable $ ?.
fuente
fuente
exit $?
(sin padres)?http://cfaj.freeshell.org/shell/cus-faq-2.html#11
¿Cómo obtengo el código de salida de
cmd1
encmd1|cmd2
Primero, tenga en cuenta que el
cmd1
código de salida podría ser distinto de cero y aún así no significa un error. Esto sucede por ejemplo enpuede observar un estado de salida 141 (o 269 con ksh93)
cmd1
, pero es porquecmd
fue interrumpido por una señal SIGPIPE cuandohead -1
terminó después de haber leído una 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 pipestatus.
cmd1
el código de salida está en$pipestatus[1]
, elcmd3
código de salida en$pipestatus[3]
, de modo que$?
siempre es el mismo que$pipestatus[-1]
.si. con golpe:
Los códigos de salida se proporcionan en la
PIPESTATUS
matriz especial.cmd1
el código de salida está en${PIPESTATUS[0]}
, elcmd3
código de salida en${PIPESTATUS[2]}
, de modo que$?
siempre es el mismo que${PIPESTATUS: -1}
....
Para más detalles ver el siguiente enlace .
fuente
para bash:
fuente
En bash, esto es fácil, solo átalos con &&:
También puede usar la construcción anidada if:
fuente
if (! command)
si espera un código de error distinto de cero del comando.fuente
$*
; use"$@"
en su lugar para preservar espacios y comodines.