¿Cuál es su método favorito para manejar errores en Bash? El mejor ejemplo de manejo de errores que he encontrado en la web fue escrito por William Shotts, Jr en http://www.linuxcommand.org .
Sugiere usar la siguiente función para el manejo de errores en Bash:
#!/bin/bash
# A slicker error handling routine
# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run. You can get this
# value from the first item on the command line ($0).
# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>
PROGNAME=$(basename $0)
function error_exit
{
# ----------------------------------------------------------------
# Function for exit due to fatal program error
# Accepts 1 argument:
# string containing descriptive error message
# ----------------------------------------------------------------
echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
exit 1
}
# Example call of the error_exit function. Note the inclusion
# of the LINENO environment variable. It contains the current
# line number.
echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."
¿Tiene una mejor rutina de manejo de errores que utiliza en los scripts de Bash?
Respuestas:
¡Usa una trampa!
... luego, cada vez que crea un archivo temporal:
y
$temp_foo
se eliminará al salir, y se imprimirá el número de línea actual. (set -e
también le dará un comportamiento de salida en caso de error, aunque viene con advertencias serias y debilita la previsibilidad y portabilidad del código).Puede dejar que la trampa lo llame
error
(en cuyo caso usa el código de salida predeterminado de 1 y ningún mensaje) o llamarlo usted mismo y proporcionar valores explícitos; por ejemplo:saldrá con el estado 2 y dará un mensaje explícito.
fuente
Esa es una buena solución. Solo quería agregar
como un mecanismo de error rudimentario. Inmediatamente detendrá su script si falla un comando simple. Creo que este debería haber sido el comportamiento predeterminado: dado que tales errores casi siempre significan algo inesperado, no es realmente "sensato" seguir ejecutando los siguientes comandos.
fuente
set -e
no está exento de problemas: consulte mywiki.wooledge.org/BashFAQ/105 para obtener más información.set -o pipefail
set -e
tiene una alta relación costo-beneficio.set -e
mí mismo, pero algunos de los otros clientes habituales en irc.freenode.org # bash aconsejan (en términos bastante fuertes) en contra. Como mínimo, las trampas en cuestión deben ser bien entendidas.Leer todas las respuestas en esta página me inspiró mucho.
Entonces, aquí está mi pista:
contenido del archivo: lib.trap.sh
Ejemplo de uso:
contenido del archivo: trap-test.sh
Corriendo:
Salida:
Como puede ver en la captura de pantalla a continuación, el resultado es de color y el mensaje de error viene en el idioma utilizado.
fuente
test ${#g_libs[@]} == 0
no es compatible con POSIX (POSIX soportes de prueba=
para las comparaciones de cadenas o-eq
para comparaciones numéricas, pero no==
, por no mencionar la falta de matrices en POSIX), y si estás no tratando de ser compatible con POSIX, por qué en el mundo estás utilizandotest
en lugar de un contexto matemático?(( ${#g_libs[@]} == 0 ))
es, después de todo, más fácil de leer.case "$(uname)" in Darwin ) stderr_log="${TMPDIR}stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac
Una alternativa equivalente a "set -e" es
Hace que el significado de la bandera sea algo más claro que simplemente "-e".
Adición aleatoria: para deshabilitar temporalmente el indicador y volver al valor predeterminado (de ejecución continua independientemente de los códigos de salida), solo use
Esto impide el manejo adecuado de errores mencionado en otras respuestas, pero es rápido y efectivo (como bash).
fuente
$(foo)
en una línea desnuda en lugar de solofoo
es generalmente lo incorrecto. ¿Por qué promoverlo dándolo como ejemplo?Inspirado por las ideas presentadas aquí, he desarrollado una forma legible y conveniente de manejar los errores en los scripts de bash en mi proyecto bash boilerplate .
Simplemente obteniendo la biblioteca, obtienes lo siguiente de la caja (es decir, detendrá la ejecución ante cualquier error, como si se usara
set -e
gracias a untrap
encendidoERR
y algo de bash-fu ):Hay algunas características adicionales que ayudan a manejar los errores, como try and catch , o la palabra clave throw , que le permite interrumpir la ejecución en un punto para ver la traza inversa. Además, si el terminal lo admite, escupe emojis powerline, colorea partes de la salida para una gran legibilidad y subraya el método que causó la excepción en el contexto de la línea de código.
La desventaja es que no es portátil, el código funciona en bash, probablemente solo> = 4 (pero imagino que podría ser portado con algún esfuerzo para bash 3).
El código está separado en varios archivos para un mejor manejo, pero me inspiró la idea de la traza de la respuesta anterior de Luca Borrione .
Para leer más o echar un vistazo a la fuente, vea GitHub:
https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw
fuente
Prefiero algo realmente fácil de llamar. Así que uso algo que parece un poco complicado, pero que es fácil de usar. Por lo general, solo copio y pego el siguiente código en mis scripts. Una explicación sigue el código.
Normalmente pongo una llamada a la función de limpieza junto a la función error_exit, pero esto varía de un guión a otro, así que lo dejé fuera. Las trampas captan las señales de terminación comunes y se aseguran de que todo se limpie. El alias es lo que hace la verdadera magia. Me gusta verificar todo por fallas. Entonces, en general, llamo a los programas en un "si!" declaración de tipo Al restar 1 del número de línea, el alias me dirá dónde ocurrió la falla. También es muy simple de llamar, y es una prueba bastante idiota. A continuación se muestra un ejemplo (simplemente reemplace / bin / false con lo que sea que vaya a llamar).
fuente
$LINENO - 1
. Mostrar correctamente sin ella.false || die "hello death"
Otra consideración es el código de salida para devolver. Simplemente "
1
" es bastante estándar, aunque hay un puñado de códigos de salida reservados que bash usa , y esa misma página argumenta que los códigos definidos por el usuario deben estar en el rango 64-113 para cumplir con los estándares C / C ++.También puede considerar el enfoque de vector de bits que
mount
utiliza para sus códigos de salida:OR
Al unir los códigos juntos, su script puede señalar múltiples errores simultáneos.fuente
Utilizo el siguiente código de captura, también permite rastrear errores a través de tuberías y comandos de 'tiempo'
fuente
function
palabra clave es gratuitamente incompatible con POSIX. Considere hacer su declaración justaerror() {
, sinfunction
antes.${$?}
debería ser$?
, o${?}
si insiste en usar aparatos innecesarios; lo interno$
está mal.he usado
antes de; Creo que porque 'salir' me estaba fallando por alguna razón. Sin embargo, los valores predeterminados anteriores parecen una buena idea.
fuente
Esto me ha servido bien por un tiempo ahora. Imprime mensajes de error o advertencia en rojo, una línea por parámetro, y permite un código de salida opcional.
fuente
No estoy seguro de si esto será útil para usted, pero modifiqué algunas de las funciones sugeridas aquí para incluir la verificación del error (código de salida del comando anterior) dentro de él. En cada "verificación" también paso como parámetro el "mensaje" de cuál es el error para fines de registro.
Ahora, para llamarlo dentro del mismo script (o en otro si lo uso
export -f error_exit
), simplemente escribo el nombre de la función y paso un mensaje como parámetro, como este:Utilizando esto, pude crear un archivo bash realmente robusto para algún proceso automatizado y se detendrá en caso de errores y me notificará (
log.sh
lo hará)fuente
function
palabras clave, soloerror_exit() {
.cd /home/myuser/afolder || error_exit "Unable to switch to folder"
?Este truco es útil para los comandos o funciones que faltan. El nombre de la función faltante (o ejecutable) se pasará en $ _
fuente
$_
estaría disponible en la función igual que$?
? No estoy seguro de que haya alguna razón para usar una en la función, pero no la otra.Esta función me ha estado sirviendo bastante bien recientemente:
Lo llama agregando 0 o el último valor de retorno al nombre del comando a ejecutar, de modo que puede encadenar comandos sin tener que verificar los valores de error. Con esto, este bloque de instrucciones:
Se convierte en esto:
Si alguno de los comandos falla, el código de error simplemente se pasa al final del bloque. Me resulta útil cuando no desea que se ejecuten comandos posteriores si falla uno anterior, pero tampoco desea que el script salga de inmediato (por ejemplo, dentro de un bucle).
fuente
Usar trap no siempre es una opción. Por ejemplo, si está escribiendo algún tipo de función reutilizable que necesita manejo de errores y que se puede invocar desde cualquier script (después de obtener el archivo con funciones auxiliares), esa función no puede asumir nada sobre el tiempo de salida del script externo, lo que hace que usar trampas sea muy difícil. Otra desventaja del uso de trampas es la mala capacidad de compilación, ya que corre el riesgo de sobrescribir la trampa anterior que podría establecerse antes en la cadena de llamadas.
Hay un pequeño truco que se puede utilizar para manejar los errores de manera adecuada sin trampas. Como ya sabrás por otras respuestas,
set -e
no funciona dentro de los comandos si usas el||
operador después de ellos, incluso si los ejecutas en una subshell; por ejemplo, esto no funcionaría:Pero el
||
operador es necesario para evitar el regreso de la función externa antes de la limpieza. El truco es ejecutar el comando interno en segundo plano y luego esperarlo inmediatamente. Elwait
incorporado devolverá el código de salida del comando interno, y ahora está usando||
despuéswait
, no la función interna, por lo queset -e
funciona correctamente dentro de este último:Aquí está la función genérica que se basa en esta idea. Debería funcionar en todos los shells compatibles con POSIX si elimina las
local
palabras clave, es decir, reemplace todolocal x=y
con solox=y
:Ejemplo de uso:
Ejecutando el ejemplo:
Lo único que debe tener en cuenta al usar este método es que todas las modificaciones de las variables de Shell realizadas desde el comando al que pasa
run
no se propagarán a la función de llamada, porque el comando se ejecuta en una subshell.fuente