Я пишу сценарий оболочки и должен проверить, установлено ли приложение терминала. Я хочу использовать команду TRY/CATCH для этого, если нет более аккуратного способа.
Есть ли команда TRY CATCH в Bash
Ответ 1
Есть ли команда TRY CATCH в Bash?
Нет.
Bash не имеет столько предметов роскоши, сколько можно найти на многих языках программирования.
В bash нет try/catch
; однако можно добиться аналогичного поведения с помощью &&
или ||
.
Использование ||
:
если command1
терпит неудачу, тогда command2
выполняется следующим образом
command1 || command2
Аналогично, использование &&
, command2
будет выполняться, если command1
успешно
Ближайшее приближение try/catch
выглядит следующим образом
{ # try
command1 &&
#save your output
} || { # catch
# save log for exception
}
Также bash содержит некоторые механизмы обработки ошибок, а также
set -e
Он немедленно остановит ваш script, если простую команду не удастся. Я думаю, что это должно было быть поведение по умолчанию. Поскольку такие ошибки почти всегда означают что-то неожиданное, на самом деле не "разумно" выполнять следующие команды.
А также почему не if...else
. Это твой лучший друг.
Ответ 2
Основываясь на некоторых ответах, которые я нашел здесь, я сделал для себя небольшой вспомогательный файл для моих проектов:
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
}
вот пример того, как он выглядит при использовании:
#!/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 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 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
}
Ответ 3
Я разработал почти безупречную реализацию 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 шаблона/рамки. Это также расширяет идею попытки и уловить такие вещи, как обработка ошибок с обратным трафиком и исключениями (плюс некоторые другие приятные функции).
Вот код, который отвечает только за попытку и улов:
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
}
Не стесняйтесь использовать, развивать и вносить вклад - на GitHub.
Ответ 4
bash
не отменяет выполняемое выполнение в случае, если sth обнаруживает состояние ошибки (если вы не установили флаг -e
). Языки программирования, предлагающие try/catch
, делают это, чтобы препятствовать "спасению" из-за этой особой ситуации (отсюда обычно называют "исключение" ).
В bash
вместо этого только команда, о которой идет речь, выйдет с кодом выхода больше 0, что указывает на состояние ошибки. Конечно, вы можете это проверить, но поскольку автоматическое спасение ни от чего не происходит, попытка/улов не имеет смысла. Это просто отсутствует в этом контексте.
Вы можете, однако, имитировать спасение, используя вспомогательные оболочки, которые могут заканчиваться в той точке, которую вы решите:
(
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"
)
...
Ответ 5
Как все говорят, bash не имеет правильного синтаксиса try/catch, поддерживаемого языком. Вы можете запустить bash с аргументом -e
или использовать set -e
внутри script, чтобы прервать весь процесс 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, но он может поцарапать подобный зуд для вас.
Ответ 6
Вы можете использовать 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
)
Ответ 7
Есть так много подобных решений, которые, вероятно, работают. Ниже приведен простой и эффективный способ выполнения 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
Ответ 8
И у вас есть ловушки http://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_12_02.html, которые не совпадают, но другой метод, который вы можете использовать для этой цели
Ответ 9
Ты можешь сделать:
#!/bin/bash
if <command> ; then # TRY
<do-whatever-you-want>
else # CATCH
echo 'Exception'
<do-whatever-you-want>
fi
Ответ 10
Очень простая вещь, которую я использую:
try() {
"[email protected]" || (e=$?; echo "[email protected]" > /dev/stderr; exit $e)
}