Existe-t-il une commande TRY CATCH dans Bash

J’écris un script shell et je dois vérifier qu’une application de terminal a été installée. Je veux utiliser une commande TRY / CATCH pour le faire, à moins que cela ne soit plus simple.

Existe-t-il une commande TRY CATCH dans Bash?

Non.

Bash n’a pas autant de luxe que l’on peut trouver dans de nombreux langages de programmation.

Il n’y a pas de try/catch dans bash; cependant, on peut obtenir un comportement similaire en utilisant && ou || .

En utilisant || :

si command1 échoue alors command2 s’exécute comme suit

 command1 || command2 

De même, en utilisant && , command2 sera exécuté si command1 réussit

L’approximation la plus proche de try/catch est la suivante

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

De plus, bash contient des mécanismes de gestion des erreurs,

 set -e 

Il arrêtera immédiatement votre script si une commande simple échoue. Je pense que cela aurait dû être le comportement par défaut. Étant donné que de telles erreurs signifient presque toujours quelque chose d’inattendu, il n’est pas vraiment raisonnable de continuer à exécuter les commandes suivantes.

Et aussi pourquoi pas if...else . C’est ton meilleur ami.

Sur la base de certaines réponses que j’ai trouvées ici, je me suis fait un petit fichier d’aide à la source pour mes projets:

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 } 

voici un exemple de son apparence:

 #!/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 } 

J’ai développé une implémentation sans erreur de try & catch dans bash, qui vous permet d’écrire du code comme:

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

Vous pouvez même imbriquer les blocs d’essai à l’intérieur d’eux-mêmes!

 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__!" } 

Le code fait partie de mon cadre / framework bash . Il étend encore l’idée de try & catch avec des choses comme la gestion des erreurs avec backtrace et des exceptions (ainsi que d’autres fonctionnalités intéressantes).

Voici le code qui est juste responsable 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 } 

N’hésitez pas à utiliser, bricoler et consortingbuer – c’est sur GitHub .

bash n’abandonne pas l’exécution en cours si sth détecte un état d’erreur (sauf si vous définissez l’indicateur -e ). Les langages de programmation qui proposent try/catch font cela pour empêcher un “renflouement” à cause de cette situation particulière (donc typiquement appelée “exception”).

Dans le bash , à la place, seule la commande en question sortira avec un code de sortie supérieur à 0, indiquant cet état d’erreur. Vous pouvez vérifier cela bien sûr, mais comme il n’y a pas de renflouement automatique, essayer / rattraper n’a pas de sens. Il manque juste ce contexte.

Vous pouvez toutefois simuler un renflouement en utilisant des sous-shells qui peuvent se terminer à un point que vous décidez:

 ( 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 

Au lieu de some_condition avec un if vous pouvez également essayer une commande, et au cas où il échoue (a un code de sortie supérieur à 0), renvoyez:

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

Malheureusement, en utilisant cette technique, vous êtes limité à 255 codes de sortie différents (1..255) et aucun object d'exception décent ne peut être utilisé.

Si vous avez besoin de plus d'informations pour passer avec votre exception simulée, vous pouvez utiliser la sortie standard des sous-couches, mais c'est un peu compliqué et peut-être une autre question 😉

En utilisant l'indicateur -e mentionné ci-dessus pour le shell, vous pouvez même supprimer cette instruction de exit explicite:

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

Comme tout le monde le dit, bash n’a pas de syntaxe try / catch prise en charge par la langue. Vous pouvez lancer bash avec l’argument -e ou utiliser set -e dans le script pour abandonner tout le processus bash si une commande a un code de sortie différent de zéro. (Vous pouvez également set +e pour autoriser temporairement les commandes défaillantes.)

Ainsi, une technique pour simuler un bloc try / catch consiste à lancer un sous-processus pour effectuer le travail avec l’ -e activée. Ensuite, dans le processus principal, vérifiez le code retour du sous-processus.

Bash prend en charge les chaînes Heredoc, vous n’avez donc pas à écrire deux fichiers distincts pour gérer cela. Dans l’exemple ci-dessous, le TRY heredoc s’exécutera dans une instance de bash distincte, avec l’ -e activée. Le sous-processus se bloquera si une commande renvoie un code de sortie différent de zéro. Ensuite, dans le processus principal, nous pouvons vérifier le code retour pour gérer un bloc catch.

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

Ce n'est pas un bloc try / catch pris en charge par la langue, mais cela peut provoquer une démangeaison similaire.

Vous pouvez utiliser le trap :

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

Se traduit par:

 ( 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 ) 

Il y a tellement de solutions similaires qui fonctionnent probablement. Vous trouverez ci-dessous un moyen simple et pratique d’essayer / rattraper, avec des explications dans les commentaires.

 #!/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 

Et vous avez des pièges http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html qui ne sont pas les mêmes, mais d’autres techniques que vous pouvez utiliser à cette fin

Tu peux faire:

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

Une chose très simple que j’utilise:

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