Точный язык, используемый в спецификации 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
опцию. К счастью, существующие оболочки в основном соответствуют своему поведению.
set -e; (cd /nonexisting)
.(Отвечая на мой вопрос, потому что я нашел решение) Одно из решений - всегда назначать это промежуточной переменной. Таким образом, код возврата (
$?
) устанавливается.Так
Будет выводить
1
(или вместо этого выходить, еслиset -e
присутствует), однако:Будет выводить
0
после пустой строки. Код возврата echo (или другой команды, которая выполнялась с выводом backtick) заменит код возврата 0.Я все еще открыт для решений, которые не требуют промежуточной переменной, но это помогает мне в этом.
источник
Как OP указал в своем собственном ответе, назначение вывода подкоманды переменной действительно решает проблему;
$?
остаются невредимыми.Тем не менее, один крайний случай все еще может озадачить вас ложными отрицаниями (т. Е. Команда не выполняется, но ошибка не всплывает),
local
объявление переменной:local myvar=$(subcommand)
будет всегда возвращаться0
!bash(1)
указывает на это:Вот простой тестовый пример:
Выход:
источник
VAR=...
иlocal VAR=...
правда меня обескуражили!Как уже говорили другие,
local
всегда будет возвращать 0. Решение состоит в том, чтобы сначала объявить переменную:Выход:
источник
Чтобы выйти из-за ошибки подстановки команд, вы можете явно указать
-e
в подоболочке:источник
Интересный момент!
Я никогда не сталкивался с этим, потому что я не являюсь другом
set -e
(вместо этого я предпочитаюtrap ... ERR
), но уже проверял это:trap ... ERR
также не улавливаю ошибки внутри$(...)
(или старомодные кавычки).Я думаю, что проблема (как это часто бывает) в том, что здесь вызывается подоболочка и
-e
явно означает текущую оболочку.Единственное другое решение, которое пришло на ум в этот момент, это использовать чтение:
Это броски
ERR
и с-e
оболочкой будут прекращены. Единственная проблема: это работает только для команд с одной строкой вывода (или вы пропускаете что-то, соединяющее строки).источник