#!/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?
TL,DR: para se beneficiar de
set -e
, atribua diretamente o resultado de uma substituição de comando a uma variável (opcionalmente com strings extras ao redor). Não combine várias substituições de comando ou 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 deset -e
(que não são específicas do bash: outros shells semelhantes a sh têm o mesmo problema).Demonstração: execute esta variante do seu segundo exemplo.
Observe que
echo >&2 after cat
não foi executado. Seria seinherit_errexit
estivesse desligado.O problema é que
set -e
só para a execução em caso de erro em casos simples. Se uma substituição de comando retornar um status de falha, isso não interrompe a execução do comando simples que contém a substituição. Ele pode, no máximo, definir o status de retorno do comando simples, que por sua vez pode interromper a execução do script. Um “comando simples” consiste em atribuições, redirecionamentos e um nome e argumentos de comando executável opcional. 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 é o status de retorno da última substituição de comando ou 0 se não houver nenhum. Aqui estão alguns exemplos de comandos simples:true </no/such/file
→ status 1 devido ao redirecionamento com falhafalse </dev/null
→ estado 1 defalse
a=b
→ status 0 por não ter nenhuma peça que possa falhara=$(exit 0) b=$(exit 1) c=$(exit 2)
→ status 2 da última substituição de comandoa=$(exit 2) b=$(exit 1) c=$(exit 0)
→ status 0 da última substituição de comandotrue $(exit 0) $(exit 1) $(exit 2)
→ estado 0 detrue
Mais uma vez, em todos os casos,
set -e
só interromperá o script 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, deecho
(exceto seecho
falhar na escrita), independente do que acontecer dentro da substituição do comando. Portanto, o script não parará aqui, mesmo queset -e
esteja ativo. Da mesma forma, no terceiro script,f $(…)
tem o status 0 def
.