Cómo verificar el estado de salida usando una instrucción if

256

Me preguntaba cuál sería la mejor manera de verificar el estado de salida en una declaración if para hacer eco de una salida específica.

Estoy pensando en que sea

if [ $? -eq 1 ]
then
   echo "blah blah blah"
fi

El problema que también tengo es que la declaración de salida es anterior a la declaración if simplemente porque tiene que tener ese código de salida. Además, sé que estoy haciendo algo mal ya que la salida obviamente saldría del programa.

deadcell4
fuente
3
Plaese publique su guión completo (o al menos un alcance más amplio). De lo contrario, esto parece estar bien.
RedX
55
Si necesita usar el código de salida de alguna invocación de programa en particular en dos lugares diferentes, entonces necesita preservarlo, algo some_program; rc=$?; if [ ${rc} -eq 1 ] .... fi ; exit ${rc}
similar a esto

Respuestas:

262

Cada comando que se ejecuta tiene un estado de salida.

Esa verificación está mirando el estado de salida del comando que terminó más recientemente antes de que se ejecute esa línea.

Si desea que su secuencia de comandos salga cuando esa prueba devuelva verdadero (el comando anterior falló), entonces ponga exit 1(o lo que sea) dentro de ese ifbloque después de echo.

Dicho esto, si está ejecutando el comando y desea probar su salida utilizando lo siguiente, a menudo es más sencillo.

if some_command; then
    echo command returned true
else
    echo command returned some error
fi

O para cambiar eso, usarlo !para la negación

if ! some_command; then
    echo command returned some error
else
    echo command returned true
fi

Sin embargo, tenga en cuenta que a ninguno de esos le importa cuál es el código de error. Si sabe que solo le importa un código de error específico, entonces debe verificarlo $?manualmente.

Etan Reisner
fuente
11
@ deadcell4 Cuando uno necesita terminar un script de shell en una falla del programa, el siguiente modismo es útila_command || return 1
gboffi
10
@gboffi returnsolo funciona en una función y un script de fuente. Necesita exitpara el otro caso (que hace demasiado en una función y un script de origen). Pero sí, ese es ciertamente un patrón razonable si no necesita ninguna limpieza específica o salida adicional.
Etan Reisner
1
Tengo que decir que dash, el shell predeterminado no interactivo en muchas distribuciones modernas de Linux, no se preocupa por la distinción entre returny exitdentro de los scripts de shell ejecutados. dashsale del script incluso si lo uso returnen él.
gboffi
¿Cuál es la lógica detrás de los dos últimos controles? Parece contrario a la intuición que la condición if <command>pasa si el código de salida es 0. En cualquier otro idioma sería al revés
sjw
3
NOTA IMPORTANTE: Esto no funcionará para tuberías. if ! some_command | some_other_commandignorará el estado de some_command. Las dos soluciones alternativas de comando son set -o pipefail(puede cambiar la funcionalidad en otras partes de su programa) o mover la ifdeclaración if [[ ${PIPESTATUS[0]} -ne 0 ]]como un comando de seguimiento separado (feo, pero funcional). Si está utilizando, set -eentonces también querrá agregar || trueal final de la tubería cuando use la segunda solución, ya que eliminar la tubería del flujo de control ofrecido por iflo contrario provocaría que salga inmediatamente.
Alex Jansen el
186

Tenga en cuenta que los códigos de salida! = 0 se utilizan para informar un error. Entonces, es mejor hacer:

retVal=$?
if [ $retVal -ne 0 ]; then
    echo "Error"
fi
exit $retVal

en vez de

# will fail for error codes > 1
retVal=$?
if [ $retVal -eq 1 ]; then
    echo "Error"
fi
exit $retVal
Oo.oO
fuente
Debe probar en retVal, porque $? después de la asignación de retVal no es el valor de retorno de su comando.
anr78
En realidad no: mywiki.wooledge.org/BashFAQ/002 ; sin embargo, estoy de acuerdo en que la edición mejora la legibilidad.
Oo.oO
1
Acabo de encontrar esta publicación que lo explica stackoverflow.com/questions/20157938/…
anr78
dnf check-updatedevuelve 0 (sin actualizaciones), 100 (actualizaciones disponibles) o 1 (error).
jww
1
@jww: bueno, no es una buena idea ir en contra de la convención ( gnu.org/software/bash/manual/html_node/Exit-Status.html ). Pero, bueno, no hay nada que lo impida. Si los dnfdesarrolladores han elegido de esta manera, es su elección. Pero aún así, su elección no hace que la especificación se rompa :)
Oo.oO
44

$?es un parámetro como cualquier otro. Puede guardar su valor para usar antes de llamar en última instancia exit.

exit_status=$?
if [ $exit_status -eq 1 ]; then
    echo "blah blah blah"
fi
exit $exit_status
chepner
fuente
29

Alternativa a la ifdeclaración explícita

Mínimamente:

test $? -eq 0 || echo "something bad happened"

Completar:

EXITCODE=$?
test $EXITCODE -eq 0 && echo "something good happened" || echo "something bad happened"; 
exit $EXITCODE
Catskul
fuente
13

Solo para agregar a la útil y detallada respuesta :

Si tiene que verificar el código de salida explícitamente, es mejor usar el operador aritmético (( ... )), de esta manera:

run_some_command
(($? != 0)) && { printf '%s\n' "Command exited with non-zero"; exit 1; }

O use una casedeclaración:

run_some_command; ec=$?  # grab the exit code into a variable so that it can
                         # be reused later, without the fear of being overwritten
case $ec in
    0) ;;
    1) printf '%s\n' "Command exited with non-zero"; exit 1;;
    *) do_something_else;;
esac

Respuesta relacionada sobre el manejo de errores en Bash:

codeforester
fuente