6

Есть ли команда TRY CATCH в Bash?

1

Я пишу оболочку и мне нужно проверить, установлено ли терминальное приложение. Я хочу использовать команду TRY/CATCH для этого, если нет более элегантного способа. Есть ли какие-то рекомендации по проверке установки приложения в оболочке?

5 ответ(ов)

9

Нет.

В Bash нет таких возможностей, как в многих других языках программирования.

Команды try/catch в Bash отсутствуют; однако, можно достичь аналогичного поведения, используя && или ||.

Используя ||:

если command1 не выполняется, то выполняется command2, как показано ниже:

command1 || command2

Аналогично, с использованием &&, command2 выполнится, если command1 завершится успешно.

Ближайшей аналогией try/catch будет следующее:

{ # try

    command1 &&
    # сохранить вывод

} || { # catch
    # сохранить лог исключения 
}

Также в Bash есть некоторые механизмы обработки ошибок:

set -e

Эта команда остановит выполнение вашего скрипта, если какая-либо простая команда завершится с ошибкой.

И, конечно, не забывайте про if...else. Это ваш лучший друг.

1

Вы можете создать небольшой вспомогательный файл 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
}

Этот подход делает вашу работу со скриптами более управляемой и позволяет лучше справляться с ошибками.

0

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 "И сделать последнее дело"
)
...
0

Вы можете использовать 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, независимо от того, произошла ошибка или нет.

0

Существует множество подобных решений, которые, вероятно, работают. Ниже приведен простой и рабочий способ реализации механизма 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

Этот подход позволяет эффективно обрабатывать ошибки при выполнении функций, обеспечивая простоту и ясность кода.

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь