set -e
завершает сценарий, если встречается ненулевой код выхода, кроме как при определенных условиях. Кратко говоря об опасности его использования: оно не ведет себя так, как думают люди.
На мой взгляд, это следует рассматривать как опасный хак, который продолжает существовать только в целях совместимости. Оператор set -e
не превращает оболочку из языка, использующего коды ошибок, в язык, использующий поток управления, подобный исключению, он просто слабо пытается эмулировать это поведение.
Грег Вулледж может многое сказать об опасностях set -e
:
Во второй ссылке есть различные примеры неинтуитивного и непредсказуемого поведения set -e
.
Некоторые примеры не интуитивного поведения set -e
(некоторые взяты из вики-ссылки выше):
установить -е
х = 0
пусть х ++
echo "x is $ x"
Вышеприведенное приведет к преждевременному завершению работы сценария оболочки, поскольку let x++
возвращает 0, которое рассматривается let
ключевым словом как ложное значение и превращается в ненулевой код завершения. set -e
замечает это и молча завершает сценарий.
установить -е
[-d / opt / foo] && echo "Предупреждение: foo уже установлен. Перезапишет." > & 2
echo "Установка foo ..."
Вышеуказанное работает как положено, выводит предупреждение, если оно /opt/foo
уже существует.
установить -е
check_previous_install () {
[-d / opt / foo] && echo "Предупреждение: foo уже установлен. Перезапишет." > & 2
}
check_previous_install
echo "Установка foo ..."
Вышеупомянутое, несмотря на то, что единственная строка была преобразована в функцию, прекратит работу, если /opt/foo
ее не существует. Это потому, что тот факт, что он работал изначально, является особым исключением из set -e
поведения России. Когда a && b
возвращается ненулевое значение, оно игнорируется set -e
. Однако теперь, когда это функция, код выхода функции равен коду выхода этой команды, и функция, возвращающая ненулевое значение, автоматически завершит выполнение сценария.
установить -е
IFS = $ '\ n' read -d '' -r -a config_vars <config
Выше будет читать массив config_vars
из файла config
. Как мог бы предположить автор, он завершается с ошибкой, если config
отсутствует. Поскольку автор, возможно, не намерен, он молча завершается, если config
не заканчивается новой строкой. Если бы set -e
они здесь не использовались, то config_vars
содержали бы все строки файла независимо от того, заканчивался ли он новой строкой.
Пользователи Sublime Text (и других текстовых редакторов, которые неправильно обрабатывают переводы строк), остерегаются.
установить -е
should_audit_user () {
local group groups = "$ (groups" $ 1 ")"
для группы в $ groups; делать
if ["$ group" = аудит]; затем верните 0; фи
сделанный
возврат 1
}
if should_audit_user "$ user"; тогда
регистратор "Бла"
фи
Автор здесь может разумно ожидать, что если по какой-то причине пользователь $user
не существует, то groups
команда завершится неудачно, и сценарий завершится, вместо того, чтобы позволить пользователю выполнить некоторую задачу без проверки. Однако в этом случае set -e
прекращение никогда не вступает в силу. Если $user
по какой-либо причине не может быть найдено, вместо завершения сценария should_audit_user
функция просто вернет неверные данные, как если бы они set -e
не действовали.
Это относится к любой функции, вызываемой из условной части if
инструкции, независимо от того, насколько глубоко она вложена, где бы она ни была определена, даже если вы set -e
снова запускаете ее внутри. Использование if
в любой точке полностью отключает эффект set -e
до тех пор, пока блок условий не будет полностью выполнен. Если автор не знает об этой ловушке или не знает весь свой стек вызовов во всех возможных ситуациях, когда функция может быть вызвана, тогда он напишет ошибочный код, и ложное чувство безопасности, обеспечиваемое, set -e
будет, по крайней мере, частично виноваты.
Даже если автор полностью осознает эту ловушку, обходной путь состоит в том, чтобы писать код так же, как если бы он был написан без него set -e
, эффективно делая этот переход менее чем бесполезным; Мало того, что автор должен написать код обработки ошибок вручную, как если бы он set -e
не был в силе, но и наличие set -e
могло ввести их в заблуждение, заставляя их думать, что они не обязаны это делать.
Некоторые дополнительные недостатки set -e
:
- Это поощряет небрежный код. Об обработчиках ошибок полностью забывают в надежде, что все, что не получилось, сообщит об ошибке некоторым разумным способом. Однако с примерами, подобными
let x++
приведенным выше, это не так. Если сценарий неожиданно умирает, он обычно молча, что затрудняет отладку. Если сценарий не умер, и вы ожидали его (см. Предыдущий пункт), то у вас в руках более тонкий и коварный баг.
- Это приводит людей к ложному чувству безопасности. Снова посмотрите
if
точку пули -условия.
- Места, где заканчивается оболочка, не согласуются между оболочками или версиями оболочки. Можно случайно написать скрипт, который ведет себя по-разному в старой версии bash из-за того,
set -e
что он был настроен между этими версиями.
set -e
Это спорный вопрос, и некоторые люди, знающие о проблемах вокруг него, рекомендуют против него, в то время как другие просто рекомендуют позаботиться, пока он активен, чтобы знать подводные камни. Есть много новичков в написании сценариев оболочки, которые рекомендуют set -e
все сценарии как универсальное средство для выявления ошибок, но в реальной жизни это не работает.
set -e
не заменит образование.