¿Hay un comando TRY CATCH en Bash?

Estoy escribiendo un script de shell y necesito verificar que se haya instalado una aplicación de terminal. Quiero usar un comando TRY / CATCH para hacer esto a menos que haya una manera más ordenada.

¿Hay un comando TRY CATCH en Bash?

No.

Bash no tiene tantos lujos como uno puede encontrar en muchos lenguajes de progtwigción.

No hay try/catch en bash; sin embargo, uno puede lograr un comportamiento similar usando && o || .

Usando || :

si command1 falla, entonces command2 ejecuta de la siguiente manera

 command1 || command2 

Del mismo modo, usando && , command2 se ejecutará si command1 tiene éxito

La aproximación más cercana de try/catch es la siguiente

 { # try command1 && #save your output } || { # catch # save log for exception } 

También bash contiene algunos mecanismos de manejo de errores, así

 set -e 

Inmediatamente detendrá tu 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.

Y también por qué no if...else . Es tu mejor amigo.

Basado en algunas respuestas que encontré aquí, me hice un pequeño archivo de ayuda para obtener mis proyectos:

trycatch.sh

 #!/bin/bash function try() { [[ $- = *e* ]]; SAVED_OPT_E=$? set +e } function throw() { exit $1 } function catch() { export ex_code=$? (( $SAVED_OPT_E )) && set +e return $ex_code } function throwErrors() { set -e } function ignoreErrors() { set +e } 

aquí hay un ejemplo de cómo se ve en uso:

 #!/bin/bash export AnException=100 export AnotherException=101 # start with a try try ( # open a subshell !!! echo "do something" [ someErrorCondition ] && throw $AnException echo "do something more" executeCommandThatMightFail || throw $AnotherException throwErrors # automaticatly end the try block, if command-result is non-null echo "now on to something completely different" executeCommandThatMightFail echo "it's a wonder we came so far" executeCommandThatFailsForSure || true # ignore a single failing command ignoreErrors # ignore failures of commands until further notice executeCommand1ThatFailsForSure local result = $(executeCommand2ThatFailsForSure) [ result != "expected error" ] && throw $AnException # ok, if it's not an expected error, we want to bail out! executeCommand3ThatFailsForSure echo "finished" ) # directly after closing the subshell you need to connect a group to the catch using || catch || { # now you can handle case $ex_code in $AnException) echo "AnException was thrown" ;; $AnotherException) echo "AnotherException was thrown" ;; *) echo "An unexpected exception was thrown" throw $ex_code # you can rethrow the "exception" causing the script to exit if not caught ;; esac } 

Desarrollé una implementación casi perfecta de try & catch en bash, que te permite escribir código como:

 try echo 'Hello' false echo 'This will not be displayed' catch echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!" 

¡Incluso puedes anidar los bloques try-catch dentro de ellos!

 try { echo 'Hello' try { echo 'Nested Hello' false echo 'This will not execute' } catch { echo "Nested Caught (@ $__EXCEPTION_LINE__)" } false echo 'This will not execute too' } catch { echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!" } 

El código es una parte de mi estándar / marco bash . Además, amplía la idea de probar y atrapar con cosas como el manejo de errores con backtrace y excepciones (además de algunas otras características agradables).

Aquí está el código que es responsable solo de try & catch:

 set -o pipefail shopt -s expand_aliases declare -ig __oo__insideTryCatch=0 # if try-catch is nested, then set +e before so the parent handler doesn't catch us alias try="[[ \$__oo__insideTryCatch -gt 0 ]] && set +e; __oo__insideTryCatch+=1; ( set -e; trap \"Exception.Capture \${LINENO}; \" ERR;" alias catch=" ); Exception.Extract \$? || " Exception.Capture() { local script="${BASH_SOURCE[1]#./}" if [[ ! -f /tmp/stored_exception_source ]]; then echo "$script" > /tmp/stored_exception_source fi if [[ ! -f /tmp/stored_exception_line ]]; then echo "$1" > /tmp/stored_exception_line fi return 0 } Exception.Extract() { if [[ $__oo__insideTryCatch -gt 1 ]] then set -e fi __oo__insideTryCatch+=-1 __EXCEPTION_CATCH__=( $(Exception.GetLastException) ) local retVal=$1 if [[ $retVal -gt 0 ]] then # BACKWARDS COMPATIBILE WAY: # export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-1)]}" # export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-2)]}" export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[-1]}" export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[-2]}" export __EXCEPTION__="${__EXCEPTION_CATCH__[@]:0:(${#__EXCEPTION_CATCH__[@]} - 2)}" return 1 # so that we may continue with a "catch" fi } Exception.GetLastException() { if [[ -f /tmp/stored_exception ]] && [[ -f /tmp/stored_exception_line ]] && [[ -f /tmp/stored_exception_source ]] then cat /tmp/stored_exception cat /tmp/stored_exception_line cat /tmp/stored_exception_source else echo -e " \n${BASH_LINENO[1]}\n${BASH_SOURCE[2]#./}" fi rm -f /tmp/stored_exception /tmp/stored_exception_line /tmp/stored_exception_source return 0 } 

Siéntase libre de usar, bifurcarse y contribuir: está en GitHub .

bash no aborta la ejecución en ejecución en caso de que detecte un estado de error (a menos que configure el -e ). Los lenguajes de progtwigción que ofrecen try/catch hacen esto para nhibernate un “rescate” debido a esta situación especial (por lo tanto, típicamente se llama “excepción”).

En el bash , en cambio, solo el comando en cuestión saldrá con un código de salida mayor que 0, lo que indica ese estado de error. Puede verificarlo por supuesto, pero como no hay rescate automático de nada, una prueba / captura no tiene sentido. Simplemente falta ese contexto.

Sin embargo, puede simular un rescate mediante el uso de subconjuntos que pueden finalizar en el momento que usted decida:

 ( echo "Do one thing" echo "Do another thing" if some_condition then exit 3 # <-- this is our simulated bailing out fi echo "Do yet another thing" echo "And do a last thing" ) # <-- here we arrive after the simulated bailing out, and $? will be 3 (exit code) if [ $? = 3 ] then echo "Bail out detected" fi 

En lugar de eso, some_condition con un if también puede probar un comando, y en caso de que falle (tiene un código de salida mayor que 0), rescatar:

 ( echo "Do one thing" echo "Do another thing" some_command || exit 3 echo "Do yet another thing" echo "And do a last thing" ) ... 

Desafortunadamente, al utilizar esta técnica, se restringe a 255 códigos de salida diferentes (1..255) y no se pueden usar objetos de excepción decentes.

Si necesita más información para pasar junto con su excepción simulada, puede usar el stdout de las subcapas, pero eso es un poco complicado y tal vez otra pregunta 😉

Usando el indicador -e mencionado anteriormente en el intérprete de comandos, puede incluso eliminar esa statement de exit explícita:

 ( set -e echo "Do one thing" echo "Do another thing" some_command echo "Do yet another thing" echo "And do a last thing" ) ... 

Como todo el mundo dice, bash no tiene una syntax try / catch adecuada soportada por el lenguaje. Puede lanzar bash con el argumento -e o usar set -e dentro del script para abortar todo el proceso bash si algún comando tiene un código de salida distinto de cero. (También puede set +e para permitir temporalmente los comandos que fallan).

Entonces, una técnica para simular un bloque try / catch es lanzar un subproceso para hacer el trabajo con -e enabled. Luego, en el proceso principal, verifique el código de retorno del subproceso.

Bash admite cadenas heredoc, por lo que no tiene que escribir dos archivos separados para manejar esto. En el siguiente ejemplo, TRY heredoc se ejecutará en una instancia de bash separada, con -e enabled, por lo que el subproceso se bloqueará si algún comando devuelve un código de salida distinto de cero. Luego, de regreso en el proceso principal, podemos verificar el código de retorno para manejar un bloque catch.

 #!/bin/bash set +e bash -e < 

No es un bloque try / catch adecuado soportado por el lenguaje, pero puede rayar un picor similar para usted.

Puedes usar trap :

try { block A } catch { block B } finally { block C }

se traduce a:

 ( set -Ee function _catch { block B exit 0 # optional; use if you don't want to propagate (rethrow) error to outer shell } function _finally { block C } trap _catch ERR trap _finally EXIT block A ) 

Hay tantas soluciones similares que probablemente funcionen. A continuación se muestra una forma simple y eficaz de lograr try / catch, con una explicación en los comentarios.

 #!/bin/bash function a() { # do some stuff here } function b() { # do more stuff here } # this subshell is a scope of try # try ( # this flag will make to exit from current subshell on any error # inside it (all functions run inside will also break on any error) set -e a b # do more stuff here ) # and here we catch errors # catch errorCode=$? if [ $errorCode -ne 0 ]; then echo "We have an error" # We exit the all script with the same error, if you don't want to # exit it and continue, just delete this line. exit $errorCode fi 

Y tiene trampas http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html que no es lo mismo, pero otra técnica que puede usar para este propósito

Tu puedes hacer:

 #!/bin/bash if  ; then # TRY  else # CATCH echo 'Exception'  fi 

Una cosa muy simple que uso:

 try() { "$@" || (e=$?; echo "$@" > /dev/stderr; exit $e) }