¿Hay un comando TRY CATCH en Bash
Estoy escribiendo un script de shell y necesito comprobar que se ha instalado una aplicación de terminal. Quiero usar un comando TRY / CATCH para hacer esto a menos que haya una manera más ordenada.
10 answers
¿Hay un comando TRY CATCH en Bash?
No.
Bash no tiene tantos lujos como uno puede encontrar en muchos lenguajes de programación.
No hay try/catch
en bash; sin embargo, uno puede lograr un comportamiento similar usando &&
o ||
.
Usando ||
:
Si command1
falla entonces command2
se 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 como sigue
{ # try
command1 &&
#save your output
} || { # catch
# save log for exception
}
También bash contiene algunos mecanismos de manejo de errores, así como
set -e
Detendrá inmediatamente su script si un comando simple falla. Creo que este debería haber sido el comportamiento predeterminado. Dado que tales errores casi siempre significan algo inesperado, no es realmente 'sano' seguir ejecutando los siguientes comandos.
Y también ¿por qué no if...else
. Es tu mejor amigo.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-05 01:53:39
Basado en algunas respuestas que encontré aquí, me hice un pequeño archivo de ayuda para obtener la fuente de 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
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-07 10:35:56
He desarrollado una implementación casi impecable 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 mismos!
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 parte de mi bash boilerplate/framework. Amplía aún más la idea de try & catch 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 intentar & captura:
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éntete libre de usar, bifurcar y contribuir - está en GitHub.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-03 22:14:23
bash
no aborta la ejecución en ejecución en caso de que sth detecte un estado de error (a menos que establezca el indicador -e
). Los lenguajes de programación que ofrecen try/catch
hacen esto para inhibir un "rescate" debido a esta situación especial (por lo tanto típicamente llamada "excepción").
En el bash
, en cambio, solo el comando en cuestión saldrá con un código de salida mayor que 0, indicando ese estado de error. Usted puede comprobar para que por supuesto, pero ya que no hay automático rescatandode cualquier cosa, un try/catch no tiene sentido. Simplemente le falta ese contexto.
Puede, sin embargo, simular un rescate mediante el uso de sub shells que pueden terminar en un punto 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 puedes probar un comando, y en caso de que falle (tenga un código de salida mayor que 0), bail out:
(
echo "Do one thing"
echo "Do another thing"
some_command || exit 3
echo "Do yet another thing"
echo "And do a last thing"
)
...
Desafortunadamente, usando esta técnica usted está restringido a 255 diferentes códigos de salida (1..255) y no se pueden utilizar 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 la bandera antes mencionada -e
al shell puedes incluso quitar esa declaración explícita exit
:
(
set -e
echo "Do one thing"
echo "Do another thing"
some_command
echo "Do yet another thing"
echo "And do a last thing"
)
...
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-25 12:15:31
Como todo el mundo dice, bash no tiene una sintaxis try/catch soportada por el lenguaje adecuado. Puede lanzar bash con el argumento -e
o usar set -e
dentro del script para abortar todo el proceso bash si cualquier comando tiene un código de salida distinto de cero. (También puede set +e
para permitir temporalmente comandos fallidos.)
Entonces, una técnica para simular un bloque try/catch es lanzar un subproceso para hacer el trabajo con -e
habilitado. Luego, en el proceso principal, verifique el código de devolución del subproceso.
Bash soporta 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
habilitado, por lo que el subproceso se bloqueará si cualquier comando devuelve un código de salida distinto de cero. Luego, de vuelta en el proceso principal, podemos verificar el código de retorno para manejar un bloque catch.
#!/bin/bash
set +e
bash -e <<TRY
echo hello
cd /does/not/exist
echo world
TRY
if [ $? -ne 0 ]; then
echo caught exception
fi
No es un bloque try/catch soportado por el lenguaje adecuado, pero puede rascar un picor similar para usted.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-28 17:43:31
Puedes usar trap
:
try { block A } catch { block B } finally { block C }
Se traduce como:
(
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
)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-06-23 23:43:53
Hay muchas soluciones similares que probablemente funcionan. A continuación se muestra una manera simple y de trabajo para lograr try/catch, con 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-06 03:12:28
Y tienes trampas http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html que no es lo mismo, pero otra técnica que se puede utilizar para este propósito
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-25 12:58:54
Puedes hacer:
#!/bin/bash
if <command> ; then # TRY
<do-whatever-you-want>
else # CATCH
echo 'Exception'
<do-whatever-you-want>
fi
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-02 19:42:26
Una cosa muy simple que uso:
try() {
"$@" || (e=$?; echo "$@" > /dev/stderr; exit $e)
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-06 13:25:09