10 ответов:
есть ли команда TRY CATCH в Bash?
нет.
Bash не имеет столько роскоши, сколько можно найти во многих языках программирования.
нет
try/catchв bash; однако, можно добиться аналогичного поведения с помощью&&или||.используя
||:если
command1Не тоcommand2работает следующим образомcommand1 || command2аналогично, используя
&&,command2будет выполняться, еслиcommand1успешносамое близкое от
try/catchследующим образом{ # try command1 && #save your output } || { # catch # save log for exception }также bash содержит некоторые механизмы обработки ошибок, а также
set -eон немедленно остановит ваш скрипт, если простая команда не выполняется. Я думаю, что это должно быть поведение по умолчанию. Поскольку такие ошибки почти всегда означают что-то неожиданное, на самом деле не "разумно" продолжать выполнять следующие команды.
а также почему бы и нет
if...else. Это твой лучший друг.
на основе некоторых ответов, которые я нашел здесь, я сделал себе небольшой вспомогательный файл для источника для моих проектов:
trycatch.sh
#!/bin/bash function try() { [[ $- = *e* ]]; SAVED_OPT_E=$? set +e } function throw() { exit } function catch() { export ex_code=$? (( $SAVED_OPT_E )) && set +e return $ex_code } function throwErrors() { set -e } function ignoreErrors() { set +e }вот пример как это выглядит в использовании:
#!/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 }
Я разработал почти безупречную реализацию try & catch в bash, которая позволяет писать код, например:
try echo 'Hello' false echo 'This will not be displayed' catch echo "Error in $__EXCEPTION_SOURCE__ at line: $__EXCEPTION_LINE__!"вы даже можете вложить блоки try-catch внутри себя!
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__!" }код является частью моего bash boilerplate / framework. Это еще больше расширяет идею 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 "" > /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= 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 }не стесняйтесь использовать, вилка и внести свой вклад-это на GitHub.
bashне прерывает выполнение в случае, если sth обнаруживает состояние ошибки (если вы не установите-eфлаг). Языки программирования, которые предлагаютtry/catchсделайте это для того, чтобы тормозят "спасение" из-за этой особой ситуации (поэтому обычно называется "исключение").на
bash, вместо этого только рассматриваемая команда выйдет с кодом выхода больше 0, указывая на это состояние ошибки. Вы можете проверить это конечно, но так там нет автоматического спасение чего-нибудь,try / catch не имеет смысла. Ему просто не хватает этого контекста.вы можете, однако, имитировать спасение С помощью вспомогательных оболочек, которые могут завершиться в точке, которую вы решите:
( 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вместо
some_conditionСifвы также можете просто выполнить команду, и в случае не (имеет код выхода больше 0), залог выход:( echo "Do one thing" echo "Do another thing" some_command || exit 3 echo "Do yet another thing" echo "And do a last thing" ) ...к сожалению, используя этот метод, вы ограничены 255 различными кодами выхода (1..255) и никакие приличные объекты исключения не могут быть использованы.
Если вам нужно больше информации, чтобы передать вместе с моделируемым исключением, вы можете использовать stdout из подрешеток, но это немного сложно и, возможно, другой вопрос ; -)
используя вышеуказанные
-eфлаг в оболочку вы можете даже лишить это явноеexitзаявление:( set -e echo "Do one thing" echo "Do another thing" some_command echo "Do yet another thing" echo "And do a last thing" ) ...
как все говорят, bash не имеет правильного синтаксиса try/catch с поддержкой языка. Вы можете запустить bash с помощью
-eаргумент или использоватьset -eвнутри скрипта, чтобы прервать весь процесс bash, если любая команда имеет ненулевой код выхода. (Вы также можетеset +eвременно разрешить невыполнение команд.)Итак, один из методов моделирования блока try / catch заключается в запуске подпроцесса для выполнения работы с
-eвключено. Затем в основном процессе проверьте код возврата подпроцесс.Bash поддерживает строки heredoc, поэтому вам не нужно писать два отдельных файла для обработки этого. В приведенном ниже примере TRY heredoc будет выполняться в отдельном экземпляре bash, с
-eвключено, поэтому подпроцесс аварийно завершит работу, если какая-либо команда возвращает ненулевой код выхода. Затем, вернувшись в основной процесс, мы можем проверить код возврата для обработки блока 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это не правильный поддерживаемый языком блок try / catch, но он может поцарапать подобный зуд для тебя.
можно использовать
trap:
try { block A } catch { block B } finally { block C }переводится как:
( 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 )
есть так много подобных решений, которые, наверное, работают. Ниже приведен простой и рабочий способ выполнения try / catch, с объяснением в комментариях.
#!/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
и у вас есть ловушки http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html что не то же самое, но другая техника, которую вы можете использовать для этой цели
вы можете сделать:
#!/bin/bash if <command> ; then # TRY <do-whatever-you-want> else # CATCH echo 'Exception' <do-whatever-you-want> fi
Comments