Какова логика выхода при неудаче в блоках и подсистемах в Bash? [дубликат]

Вопрос или проблема

В блоках Bash {} и подпроцессах () преждевременный выход не срабатывает, если за ним следует условие OR. Например, возьмите

set -e
{ echo a; false; echo b; } || echo c 

выводит

a
b

и

set -e
{ echo a; false; echo b; false;} || echo c 

выводит

a
b
c

Кажется, что учитывается только код выхода последней выполненной команды. Хотя это имеет смысл, учитывая, что используется точка с запятой вместо &&, я ожидал, что set -e все равно заставит выйти при первом false и выполнит обработку ошибок echo c.

Использование && вместо ; действительно заставляет это работать, но это усложняет ситуацию, когда есть многострочные блоки. Добавление set -e в начале блока/подпроцесса также не дает эффекта.

Причина, по которой это меня смущает, заключается в том, что

set -e
{ echo a; false; echo b; }

выводит

a

что означает, что выход при сбое работает, когда за ним не следует код ||. Так что я ожидал, что это будет иметь место с кодом || после него, выполняя его после первого сбоя в блоке. Разве нет способа достичь этого без добавления && после каждой строки в блоке?

Ответ или решение

Вопрос, поднятый вами о поведении Bash в отношении выхода из блоков и подссылок при возникновении ошибки, действительно интересен и требует детального рассмотрения. Основная суть проблемы заключается в том, что команда set -e, предназначенная для прерывания выполнения скрипта при возникновении ошибки, может вести себя иначе в блоках кода и при использовании логических операторов && и ||. Давайте подробно разберем эту логику.

Логика работы set -e

Когда вы устанавливаете флаг set -e, Bash прекращает выполнение скрипта, как только одна из команд возвращает ненулевой код возврата (что обычно указывает на ошибку). Однако это правило работает не всегда, особенно в контексте комбинирования команд с использованием логических операторов.

Рассмотрим ваш пример:

set -e
{ echo a; false; echo b; } || echo c

Здесь, несмотря на то что команда false возвращает ненулевой код выхода и, казалось бы, должна остановить выполнение блока, Bash ведет себя иначе из-за конструкции с ||. В этом случае Bash интерпретирует весь блок { ... } как одну команду. Поскольку перед || имеется команда, которая, в данном случае, успешно завершается (то есть echo a, которая успешно выводит ‘a’), enfin, set -e не завершает выполнение скрипта после ошибки false. Вместо этого, он обрабатывает код выхода блока и принимает его как успех, что в итоге приводит к тому, что команда echo b также выполняется.

Аналогичная ситуация наблюдается в вашем следующем примере:

set -e
{ echo a; false; echo b; false;} || echo c

В этом случае команда false в конце блока ничего не меняет, так как выполнение блока { ... } уже завершилось, и её код не учитывается при интерпретации условия ||.

Почему && работает иначе

Когда вы заменяете || на &&, логика выполняется иначе:

set -e
{ echo a; false; echo b; } && echo c

Здесь, если блок { ... } завершится с ошибкой, то echo c не будет выполнена, потому что результат условия && будет ложным. Таким образом, после возникновения ошибки выполнение останавливается, и последующие команды не выполняются. Это и объясняет, почему добавление && создает более очевидное поведение при наличии ошибок.

Как добиться желаемого поведения

Если вы хотите, чтобы Bash возвращал в конце блока после ошибки, и выполнять последующий код, можно воспользоваться следующими подходами:

  1. Использование команд && вместо ;: как было показано выше, комбинирование с && будет корректно обрабатывать ошибки.

  2. Пространственные блоки: вы можете обернуть ваши команды в функцию и вызывать ее, что разрешает использование set -e:

set -e
my_function() {
    echo a
    false
    echo b
}
my_function || echo c

В этом случае, когда команда false возвращает ошибку, вы сможете корректно обработать её в ||.

Заключение

Поведение set -e в Bash может сбивать с толку, особенно в контексте блоков и логических операторов. Понимание того, как Bash интерпретирует коды выхода команд в закрытых блоках и подшеллах, поможет вам правильно строить свои скрипты. Будьте внимательны к тому, как вы используете логические операторы, и попробуйте оборачивать команды в функции, чтобы избежать неожиданных результатов в вашем коде.

Оцените материал
Добавить комментарий

Капча загружается...