O idioma exato usado na especificação Single UNIX para descrever o significado deset -e
é:
Quando esta opção está ativada, se um comando simples falhar por algum dos motivos listados em Consequências de erros do shell ou retornar um valor de status de saída> 0 e não for [um comando condicional ou negado], o shell deverá sair imediatamente.
Há uma ambiguidade quanto ao que acontece quando esse comando ocorre em um subshell . Do ponto de vista prático, tudo o que o subshell pode fazer é sair e retornar um status diferente de zero ao shell pai. A saída do shell pai, por sua vez, depende se esse status diferente de zero se traduz em um comando simples que falha no shell pai.
Um desses casos problemáticos é o que você encontrou: um status de retorno diferente de zero de uma substituição de comando . Como esse status é ignorado, ele não faz com que o shell pai saia. Como você já descobriu , uma maneira de levar em consideração o status de saída é usar a substituição de comando em uma atribuição simples : o status de saída da atribuição é o status de saída da última substituição de comando nas atribuições .
Observe que isso funcionará como pretendido apenas se houver uma substituição de comando único, pois apenas o status da última substituição será levado em consideração. Por exemplo, o seguinte comando é bem-sucedido (de acordo com o padrão e em todas as implementações que eu já vi):
a=$(false)$(echo foo)
Outro caso para assistir é subshells explícitas : (somecommand)
. De acordo com a interpretação acima, o subshell pode retornar um status diferente de zero, mas como esse não é um comando simples no shell pai, o shell pai deve continuar. De fato, todas as conchas que conheço fazem o pai retornar neste momento. Embora isso seja útil em muitos casos, como (cd /some/dir && somecommand)
onde os parênteses são usados para manter uma operação, como um diretório atual, altere o local, ela viola a especificação se set -e
estiver desativada no subshell ou se o subshell retornar um status diferente de zero de uma maneira que não o encerraria, como usar !
um comando true. Por exemplo, todos os ash, bash, pdksh, ksh93 e zsh saem sem exibir foo
os seguintes exemplos:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
No entanto, nenhum comando simples falhou enquanto set -e
estava em vigor!
Um terceiro caso problemático são os elementos de um pipeline não trivial . Na prática, todos os shells ignoram falhas dos elementos do pipeline que não sejam o último e exibem um dos dois comportamentos em relação ao último elemento do pipeline:
- O ATT ksh e zsh, que executam o último elemento do pipeline no shell pai, fazem negócios da maneira usual: se um comando simples falhar no último elemento do pipeline, o shell executando esse comando, que passa a ser o shell pai, saídas.
- Outras shells aproximam o comportamento saindo se o último elemento do pipeline retornar um status diferente de zero.
Como antes, desligar set -e
ou usar uma negação no último elemento do pipeline faz com que ele retorne um status diferente de zero de uma maneira que não encerre o shell; shells diferentes de ATT ksh e zsh sairão.
A pipefail
opção do Bash faz com que um pipeline saia imediatamente abaixo set -e
se algum de seus elementos retornar um status diferente de zero.
Observe que, como uma complicação adicional, o bash é desativado set -e
em subshells, a menos que esteja no modo POSIX ( set -o posix
ou POSIXLY_CORRECT
no ambiente quando o bash é iniciado).
Tudo isso mostra que a especificação POSIX, infelizmente, faz um mau trabalho ao especificar a -e
opção. Felizmente, as conchas existentes são principalmente consistentes em seu comportamento.