Eu deparei com alguns scripts como este recentemente:
( set -e ; do-stuff; do-more-stuff; ) || echo failed
Isso parece bom para mim, mas não funciona! O set -e
não se aplica quando você adiciona o ||
. Sem isso, funciona bem:
$ ( set -e; false; echo passed; ); echo $?
1
No entanto, se eu adicionar o ||
, o set -e
será ignorado:
$ ( set -e; false; echo passed; ) || echo failed
passed
O uso de um shell separado e real funciona conforme o esperado:
$ sh -c 'set -e; false; echo passed;' || echo failed
failed
Eu tentei isso em vários shell diferentes (bash, dash, ksh93) e todos se comportam da mesma maneira, por isso não é um bug. Alguém pode explicar isso?
shell
shell-script
Cientista maluco
fonte
fonte
||
externa do subconjunto afeta o comportamento dentro do subconjunto.(set -e; echo 1; false; echo 2)
com(set -e; echo 1; false; echo 2) || echo 3
Respostas:
De acordo com este segmento , é o comportamento que o POSIX especifica para usar "
set -e
" em um subshell.(Fiquei surpreso também.)
Primeiro, o comportamento:
O segundo post observa,
Há um pouco mais no quarto post, também de Eric Blake,
Esse comportamento é definitivamente surpreendente. É contra-intuitivo: seria de esperar que a reativação
set -e
tivesse um efeito e que o contexto circundante não fosse precedente; além disso, a redação do padrão POSIX não torna isso particularmente claro. Se você lê-lo no contexto em que o comando está falhando, a regra não se aplica: ela se aplica apenas ao contexto circundante, no entanto, se aplica a ela completamente.fonte
set -e; (false; echo passed;) || echo failed
. Não me surpreende, na verdade, que -e seja ignorado neste caso, dada a redação do padrão. No meu caso, porém, estou explicitamente definindo -e no subshell e esperando que o subshell saia com falha. Não há AND-OR lista está no subnível ...if
e||
e&&
são infecciosas? isso é um absurdoDe fato,
set -e
não tem efeito dentro de subcascas se você usar o||
operador após elas; por exemplo, isso não funcionaria:Aaron D. Marasco em sua resposta faz um ótimo trabalho ao explicar por que se comporta dessa maneira.
Aqui está um pequeno truque que pode ser usado para corrigir isso: execute o comando interno em segundo plano e espere imediatamente por ele. O
wait
builtin retornará o código de saída do comando interno, e agora você está usando||
depoiswait
, não a função interna, portanto,set -e
funciona corretamente dentro do último:Aqui está a função genérica que se baseia nessa idéia. Ele deve funcionar em todos os shells compatíveis com POSIX se você remover
local
palavras-chave, ou seja, substitua todoslocal x=y
por apenasx=y
:Exemplo de uso:
Executando o exemplo:
A única coisa que você precisa estar ciente ao usar esse método é que todas as modificações das variáveis do Shell feitas a partir do comando para o qual você passa
run
não serão propagadas para a função de chamada, porque o comando é executado em um subshell.fonte
Eu não descartaria que é um bug apenas porque várias conchas se comportam dessa maneira. ;-)
Tenho mais diversão para oferecer:
Posso citar o man bash (4.2.24):
Talvez a avaliação de vários comandos leve a ignorar o || contexto.
fonte
eval
truque não funciona para mim. Eu tentei bash, bash no modo posix e dash.set -e
.set -e
está quebrado por design. Eu não o usaria para nada além dos scripts shell mais simples sem estruturas de controle, subshells ou substituições de comandos.Solução alternativa quando usar o nível superior
set -e
Cheguei a esta pergunta porque estava usando
set -e
como método de detecção de erros:e sem
||
, o script parava de rodar e nunca chegavado_more_stuff
.Como parece não haver uma solução limpa, acho que vou fazer apenas um simples
set +e
em meus scripts:fonte