Есть ли команда TRY CATCH в Bash?
Я пишу оболочку и мне нужно проверить, установлено ли терминальное приложение. Я хочу использовать команду TRY/CATCH для этого, если нет более элегантного способа. Есть ли какие-то рекомендации по проверке установки приложения в оболочке?
5 ответ(ов)
Нет.
В Bash нет таких возможностей, как в многих других языках программирования.
Команды try/catch
в Bash отсутствуют; однако, можно достичь аналогичного поведения, используя &&
или ||
.
Используя ||
:
если command1
не выполняется, то выполняется command2
, как показано ниже:
command1 || command2
Аналогично, с использованием &&
, command2
выполнится, если command1
завершится успешно.
Ближайшей аналогией try/catch
будет следующее:
{ # try
command1 &&
# сохранить вывод
} || { # catch
# сохранить лог исключения
}
Также в Bash есть некоторые механизмы обработки ошибок:
set -e
Эта команда остановит выполнение вашего скрипта, если какая-либо простая команда завершится с ошибкой.
И, конечно, не забывайте про if...else
. Это ваш лучший друг.
Вы можете создать небольшой вспомогательный файл trycatch.sh
, который поможет вам обрабатывать исключения в ваших скриптах на Bash. Основные функции, используемые в этом файле, включают try
, throw
, catch
, throwErrors
и ignoreErrors
.
Вот пример кода для вашего вспомогательного файла:
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
# Начинаем с try
try
( # открываем подшелл !!!
echo "выполняем что-то"
[ someErrorCondition ] && throw $AnException
echo "выполняем что-то еще"
executeCommandThatMightFail || throw $AnotherException
throwErrors # автоматически завершит блок try, если результат команды не null
echo "а теперь что-то совершенно другое"
executeCommandThatMightFail
echo "чудо, что мы прошли так далеко"
executeCommandThatFailsForSure || true # игнорируем одну ошибку
ignoreErrors # игнорируем ошибки команд до дальнейшего указания
executeCommand1ThatFailsForSure
local result=$(executeCommand2ThatFailsForSure)
[ "$result" != "expected error" ] && throw $AnException # если это не ожидаемая ошибка, завершение скрипта!
executeCommand3ThatFailsForSure
# следим за тем, чтобы очистить $ex_code, иначе catch * выполнится
echo "завершено"
)
# сразу после закрытия подшелла необходимо соединить группу с catch с помощью ||
catch || {
# теперь вы можете обработать исключение
case $ex_code in
$AnException)
echo "Сгенерировано исключение AnException"
;;
$AnotherException)
echo "Сгенерировано исключение AnotherException"
;;
*)
echo "Сгенерировано неожиданное исключение"
throw $ex_code # вы можете снова выбросить "исключение", чтобы скрипт завершился, если не пойман
;;
esac
}
Этот подход делает вашу работу со скриптами более управляемой и позволяет лучше справляться с ошибками.
bash
не прекращает выполнение в случае, если что-то обнаруживает состояние ошибки (если, конечно, не включить флаг -e
). Языки программирования, которые предлагают конструкцию try/catch
, делают это для того, чтобы предотвратить "выбрасывание" из-за этой особой ситуации (поэтому обычно это называется "исключение").
В bash
, наоборот, только команда, вызвавшая ошибку, завершится с кодом возврата больше 0, что указывает на состояние ошибки. Вы, конечно, можете это проверить, но поскольку нет автоматического выбрасывания из чего-либо, конструкция try/catch здесь не имеет смысла. Просто отсутствует соответствующий контекст.
Тем не менее, вы можете смоделировать выбрасывание, используя подсистемы, которые могут завершаться в точке, которую вы решите:
(
echo "Сделать кое-что"
echo "Сделать еще кое-что"
if some_condition
then
exit 3 # <-- это наше смоделированное выбрасывание
fi
echo "Сделать еще одно дело"
echo "И сделать последнее дело"
) # <-- здесь мы приходим после смоделированного выбрасывания, и $? будет 3 (код завершения)
if [ $? = 3 ]
then
echo "Выбрасывание обнаружено"
fi
Вместо условия some_condition
с if
, вы также можете просто попробовать команду, и в случае, если она провалится (вернет код завершения больше 0), выброситься:
(
echo "Сделать кое-что"
echo "Сделать еще кое-что"
some_command || exit 3
echo "Сделать еще одно дело"
echo "И сделать последнее дело"
)
...
К сожалению, при использовании этой техники вы ограничены 255 различными кодами завершения (1..255), и использовать разумные объекты исключений не представляется возможным.
Если вам нужно передать больше информации вместе с вашим смоделированным исключением, вы можете использовать stdout подсистем, но это немного сложнее и, возможно, является другой темой для обсуждения 😉
Используя упомянутый выше флаг -e
в оболочке, вы даже можете убрать явное выражение exit
:
(
set -e
echo "Сделать кое-что"
echo "Сделать еще кое-что"
some_command
echo "Сделать еще одно дело"
echo "И сделать последнее дело"
)
...
Вы можете использовать trap
для обработки ошибок и выполнения кода в блоке finally
. Вот как код на JavaScript, использующий конструкции try
, catch
и finally
, можно перевести на bash:
(
set -Ee
function _catch {
block B
exit 0 # необязательно; используйте, если не хотите передавать (повторно выбрасывать) ошибку наружному шеллу
}
function _finally {
block C
}
trap _catch ERR
trap _finally EXIT
block A
)
Этот код выполняет block A
, и если возникает ошибка, выполняется block B
, после чего всегда будет выполнен block C
, независимо от того, произошла ошибка или нет.
Существует множество подобных решений, которые, вероятно, работают. Ниже приведен простой и рабочий способ реализации механизма try/catch
в bash, с пояснениями в комментариях.
#!/bin/bash
function a() {
# здесь делаем какие-то действия
}
function b() {
# здесь делаем ещё какие-то действия
}
# этот подшелл — это область видимости для try
# try
(
# этот флаг заставляет выходить из текущего подшелла при любой ошибке
# внутри него (все функции будут также прерваны при любой ошибке)
set -e
a
b
# здесь можно выполнять дополнительные действия
)
# а здесь мы ловим ошибки
# catch
errorCode=$?
if [ $errorCode -ne 0 ]; then
echo "У нас ошибка"
# Здесь мы выходим из всего скрипта с тем же кодом ошибки, если вы
# не хотите выходить и хотите продолжить выполнение, просто удалите эту строку.
exit $errorCode
fi
Этот подход позволяет эффективно обрабатывать ошибки при выполнении функций, обеспечивая простоту и ясность кода.
Как выводить команды оболочки по мере их выполнения
Назначение значений по умолчанию для переменных оболочки одной командой в bash
Как указать приватный SSH-ключ для выполнения команды shell в Git?
Как сделать паузу в shell-скрипте на одну секунду перед продолжением?
Как использовать 'grep' для непрерывного потока?