Is there a TRY CATCH command in Bash
Is there a TRY CATCH command in Bash?
No.
Bash doesn't have as many luxuries as one can find in many programming languages.
There is no try/catch
in bash; however, one can achieve similar behavior using &&
or ||
.
Using ||
:
if command1
fails then command2
runs as follows
command1 || command2
Similarly, using &&
, command2
will run if command1
is successful
The closest approximation of try/catch
is as follows
{ # try command1 && #save your output} || { # catch # save log for exception }
Also bash contains some error handling mechanisms, as well
set -e
it stops your script if any simple command fails.
And also why not if...else
. It is your best friend.
Based on some answers I found here, I made myself a small helper file to source for my projects:
trycatch.sh
#!/bin/bashfunction 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}
here is an example how it looks like in use:
#!/bin/bashexport AnException=100export AnotherException=101# start with a trytry( # 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}
I've developed an almost flawless try & catch implementation in bash, that allows you to write code like:
try echo 'Hello' false echo 'This will not be displayed'catch echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"
You can even nest the try-catch blocks inside themselves!
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__!"}
The code is a part of my bash boilerplate/framework. It further extends the idea of try & catch with things like error handling with backtrace and exceptions (plus some other nice features).
Here's the code that's responsible just for try & catch:
set -o pipefailshopt -s expand_aliasesdeclare -ig __oo__insideTryCatch=0# if try-catch is nested, then set +e before so the parent handler doesn't catch usalias 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}
Feel free to use, fork and contribute - it's on GitHub.