
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
a=$(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
f() { :; }
f $(cat no-such-file)
echo survived
$ /tmp/a.sh
cat: no-such-file: No such file or directory
survived
Есть ли еще случаи? Или какие-то обобщения?
решение1
TL,DR: чтобы воспользоваться set -e
, напрямую присвойте результат подстановки команды переменной (возможно, с дополнительными строками вокруг нее). Не объединяйте несколько подстановок команд вместе и не используйте подстановку команды в аргументе команды.
Проблема не в inherit_errexit
. Он работает. Проблема в ограничениях set -e
(которые не являются специфичными для bash: другие sh-подобные оболочки имеют ту же проблему).
Демонстрация: запустите этот вариант вашего второго примера.
$ cat b2.sh
#!/usr/bin/env bash
set -e
shopt -s inherit_errexit
echo -n $(cat no-such-file; echo >&2 after cat)
echo survived
$ ./b2.sh
cat: no-such-file: No such file or directory
survived
Обратите внимание, что echo >&2 after cat
не было выполнено. Было бы, если бы inherit_errexit
было выключено.
Проблема в том, что set -e
останавливает выполнение при ошибке только в простых случаях. Если подстановка команды возвращает статус сбоя, это не останавливает выполнение простой команды, содержащей подстановку. Это может, самое большее, установить статус возврата простой команды, что, в свою очередь, может остановить выполнение скрипта. «Простая команда» состоит из назначений, перенаправлений и необязательного имени исполняемой команды и аргументов. Если перенаправление не удается, статус возврата равен 1. В противном случае, если есть имя команды, статус возврата простой команды равен статусу возврата исполняемой команды. В противном случае статус возврата равен статусу возврата последней подстановки команды или 0, если ее нет. Вот несколько примеров простых команд:
true </no/such/file
→ статус 1 из-за неудачного перенаправленияfalse </dev/null
→ статус 1 изfalse
a=b
→ статус 0 из-за отсутствия какой-либо детали, которая может выйти из строяa=$(exit 0) b=$(exit 1) c=$(exit 2)
→ статус 2 из последней подстановки командыa=$(exit 2) b=$(exit 1) c=$(exit 0)
→ статус 0 из последней подстановки командыtrue $(exit 0) $(exit 1) $(exit 2)
→ статус 0 изtrue
Еще раз, во всех случаях, set -e
остановит скрипт только если статус команды ненулевой. Статус встроенных команд не имеет прямого значения.
И поэтому во втором скрипте echo -n $(…)
имеет статус 0, from echo
(за исключением случаев, когда echo
не удается записать), независимо от того, что происходит внутри подстановки команды. Поэтому скрипт не остановится здесь, даже если set -e
активен. Аналогично, в третьем скрипте f $(…)
имеет статус 0 from f
.