precios - shell traduccion
forma adecuada de detectar el código de salida del shell cuando se establece la opción errexit (10)
Prefiero escribir un código de shell sólido , por lo que el errexit y el conjunto de nombres siempre se establecen.
El siguiente código se detendrá en la línea bad_command
#!/bin/bash
set -o errexit ; set -o nounset
bad_command # stop here
good_command
Quiero capturarlo, aquí está mi método
#!/bin/bash
set -o errexit ; set -o nounset
rc=1
bad_command && rc=0 # stop here
[ $rc -ne 0 ] && do_err_handle
good_command
¿Hay algún método mejor o más limpio?
Mi respuesta:
#!/bin/bash
set -o errexit ; set -o nounset
if ! bad_command ; then
# error handle here
fi
good_command
¿Qué tal esto? Si quieres el código de salida real ...
#!/bin/sh
set -e
cat /tmp/doesnotexist && rc=$? || rc=$?
echo exitcode: $rc
cat /dev/null && rc=$? || rc=$?
echo exitcode: $rc
Salida:
cat: /tmp/doesnotexist: No such file or directory
exitcode: 1
exitcode: 0
¿y si quieres saber el estado de salida de bad_command?
Creo que la forma más simple es desactivar errexit:
#!/bin/sh
set -o errexit
some_code_here
set +o errexit
bad_command
status=$?
set -o errexit
process $status
Construí un ejemplo de libro de texto (con suerte) de todas las respuestas:
#!/usr/bin/env bash
# exit immediately on error
set -o errexit
file=''dfkjlfdlkj''
# Turn off ''exit immediately on error'' during the command substitution
blah=$(set +o errexit && ls $file) && rc=$? || rc=$?
echo $blah
# Do something special if $rc
(( $rc )) && echo failure && exit 1
echo success
Continúa escribiendo código de shell sólido.
Lo haces bien si bad_command es realmente un comando. Pero tenga cuidado con las llamadas a funciones en if, while, ||, && o!, Porque errexit no funcionará allí. Puede ser peligroso.
Si tu bad_command es en realidad bad_function deberías escribir esto:
set -eu
get_exit_code() {
set +e
( set -e;
"$@"
)
exit_code=$?
set -e
}
...
get_exit_code bad_function
if [ "$exit_code" != 0 ]; then
do_err_handle
fi
Esto funciona bien en bash 4.0. En bash 4.2 solo obtienes los códigos de salida 0 o 1.
De acuerdo con los comentarios, así que si puedes renunciar a errexit
entonces puedes acortar fácilmente tu código a
bad_command || do_err_handle
good_command
Espero que esto ayude.
En bash puedes usar la trampa incorporada que es bastante agradable. Ver https://unix.stackexchange.com/questions/79648/how-to-trigger-error-using-trap-command
No estoy seguro de lo portátil que es con otras conchas ... entonces YMMV
La respuesta aceptada es buena, pero creo que podría ser refactorizada para ser aún mejor; más genérico, más fácil de refactorizar y leer:
some_command_status=$(some_command && echo $? || echo $?)
vs.
some_command && some_command_status=$? || some_command_status=$?
Mantente con errexit. Puede ayudar a encontrar errores que de otra manera podrían tener resultados impredecibles (y difíciles de detectar).
#!/bin/bash
set -o errexit ; set -o nounset
bad_command || do_err_handle
good_command
Lo anterior funcionará bien. errexit
solo requiere que la línea pase, como lo haces con bad_command && rc=0
. Por lo tanto, lo anterior con ''o'' solo ejecutará do_err_handle si bad_command
falla y, siempre que do_err_handle tampoco ''falle'', entonces la secuencia de comandos continuará.
Una forma común de evitar salir de un programa Bash cuando se establece errexit
se errexit
un comando que puede fallar es antes que el comando !
.
Después de que el comando haya ejecutado $?
no contiene el estado de salida del comando. (Contiene 0 si el comando falló y 1 en caso contrario). Sin embargo, el conjunto PIPESTATUS contiene el estado de salida del comando. Una forma segura de capturar el estado de salida de un comando que puede fallar, ya sea que errexit
esté o no configurado, es:
! bad_command
rc=${PIPESTATUS[0]}
La segunda línea se puede simplificar a rc=$PIPESTATUS
, pero Shellcheck se quejará de ello.
Si (como suele ser el caso) no necesita conocer el estado de salida del comando, solo si tuvo éxito o falló, entonces la solución de @george es buena si el controlador de errores es de una sola línea. Para los manejadores de errores de varias líneas, una buena opción es:
if ! bad_command ; then
# Handle errors here
fi
Tenga en cuenta que (excepto en circunstancias muy inusuales) no sería correcto utilizar `bad_command`
(como se sugiere en la pregunta) en lugar de bad_command
normal. Puede usar ${PIPESTATUS[0]}
para obtener el estado de salida del comando si es necesario en el código de manejo de errores, ya que $?
tampoco lo contiene en este caso.
set -o errexit bad_command || { resp_code=$? echo Bad Thing $resp_code happened }