Quando inherit_errexit não funciona?

Quando inherit_errexit não funciona?
#!/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

Algum outro caso? Ou alguma generalização?

Responder1

TL,DR: para se beneficiar set -e, atribua diretamente o resultado de uma substituição de comando a uma variável (opcionalmente com strings extras ao seu redor). Não combine várias substituições de comando nem use uma substituição de comando em um argumento de comando.

O problema não é com inherit_errexit. Está funcionando. O problema são as limitações de set -e(que não são específicas do bash: outros shells do tipo sh têm o mesmo problema).

Demonstração: execute esta variante do seu segundo exemplo.

$ 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

Observe que echo >&2 after catnão foi executado. Seria se inherit_errexitestivesse desligado.

O problema é que set -esó interrompe a execução em caso de erro em casos simples. Se uma substituição de comando retornar um status de falha, isso não interromperá a execução do comando simples que contém a substituição. Pode, no máximo, definir o status de retorno do comando simples, o que pode, por sua vez, interromper a execução do script. Um “comando simples” consiste em atribuições, redirecionamentos e um nome de comando executável opcional e argumentos. Se um redirecionamento falhar, o status de retorno será 1. Caso contrário, se houver um nome de comando, o status de retorno do comando simples será o status de retorno do comando executável. Caso contrário, o status de retorno será o status de retorno da última substituição de comando ou 0 se não houver nenhuma. Aqui estão alguns exemplos de comandos simples:

  • true </no/such/file→ status 1 devido à falha no redirecionamento
  • false </dev/null→ status 1 defalse
  • a=b→ status 0 por não possuir nenhuma peça que possa falhar
  • a=$(exit 0) b=$(exit 1) c=$(exit 2)→ status 2 da última substituição de comando
  • a=$(exit 2) b=$(exit 1) c=$(exit 0)→ status 0 da última substituição de comando
  • true $(exit 0) $(exit 1) $(exit 2)→ status 0 detrue

Mais uma vez, em todos os casos, set -eo script só será interrompido se o status do comando for diferente de zero. O status dos comandos incorporados não é diretamente relevante.

E assim no seu segundo script echo -n $(…)tem o status 0, from echo(exceto se echofalhar ao escrever), independente do que aconteça dentro da substituição do comando. Portanto o script não irá parar aqui mesmo se set -eestiver ativo. Da mesma forma, no terceiro script, f $(…)tem o status 0 de f.

informação relacionada