Когда inherit_errexit не работает?

Когда inherit_errexit не работает?
#!/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.

Связанный контент