Точный язык, используемый в спецификации Single UNIX для описания значенияset -e
:
Когда эта опция включена , если простая команда завершается неудачно по какой-либо из причин, перечисленных в « Последствиях ошибок оболочки», или возвращает значение состояния выхода> 0 и не является [условной или отрицательной командой], оболочка должна немедленно завершиться.
Существует двусмысленность относительно того, что происходит, когда такая команда происходит в подоболочке . С практической точки зрения все, что может сделать подоболочка - это выйти и вернуть ненулевой статус родительской оболочке. Завершение работы родительской оболочки зависит от того, преобразуется ли этот ненулевой статус в простую команду, не выполняющуюся в родительской оболочке.
Одним из таких проблемных случаев является тот, с которым вы столкнулись: ненулевой статус возврата из подстановки команд . Поскольку этот статус игнорируется, он не приводит к закрытию родительской оболочки. Как вы уже обнаружили , одним из способов учета состояния выхода является использование подстановки команд в простом назначении : тогда выходным состоянием назначения является статус выхода последней подстановки команд в назначении (ях) .
Обратите внимание, что это будет работать так, как задумано, только в случае замены одной команды, поскольку учитывается только состояние последней замены. Например, следующая команда является успешной (как в соответствии со стандартом, так и в каждой реализации, которую я видел):
a=$(false)$(echo foo)
Еще один случай , чтобы наблюдать за это явные Подоболочки : (somecommand)
. Согласно приведенной выше интерпретации, подоболочка может возвращать ненулевой статус, но, поскольку это не простая команда в родительской оболочке, родительская оболочка должна продолжаться. Фактически, все снаряды, о которых я знаю, возвращают родителей в этот момент. Хотя это полезно во многих случаях, например, (cd /some/dir && somecommand)
когда круглые скобки используются для сохранения локальной операции, такой как изменение текущего каталога, это нарушает спецификацию, если set -e
она отключена в подоболочке, или если подоболочка возвращает ненулевой статус таким образом, что не будет прервать его, например, при использовании !
истинной команды. Например, все команды ash, bash, pdksh, ksh93 и zsh выходят без отображения foo
в следующих примерах:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
Пока ни одна простая команда не провалилась, пока set -e
она действовала!
Третий проблемный случай - это элементы в нетривиальном конвейере . На практике все оболочки игнорируют отказы элементов конвейера, кроме последнего, и демонстрируют одно из двух поведений относительно последнего элемента конвейера:
- ATT ksh и zsh, которые выполняют последний элемент конвейера в родительской оболочке, работают как обычно: если простая команда завершается неудачно в последнем элементе конвейера, оболочка, выполняющая эту команду, которая оказывается родительской оболочкой, выходы.
- Другие оболочки приближают поведение, завершаясь, если последний элемент конвейера возвращает ненулевое состояние.
Как и раньше, отключение set -e
или использование отрицания в последнем элементе конвейера заставляет его возвращать ненулевой статус таким образом, который не должен завершать оболочку; затем выйдут другие оболочки кроме ATT ksh и zsh.
pipefail
Опция Bash приводит к немедленному завершению конвейера, set -e
если какой-либо из его элементов возвращает ненулевое состояние.
Обратите внимание, что в качестве дополнительного осложнения bash отключается set -e
в подоболочках, если он не находится в режиме POSIX ( set -o posix
или не находится POSIXLY_CORRECT
в среде, когда запускается bash).
Все это показывает, что спецификация POSIX, к сожалению, плохо определяет -e
опцию. К счастью, существующие оболочки в основном соответствуют своему поведению.