De acordo com este manual de referência :
-E (também -o errtrace)
Se definido, qualquer interceptação no ERR é herdada pelas funções do shell, substituições de comandos e comandos executados em um ambiente de subshell. A interceptação de ERR normalmente não é herdada nesses casos.
No entanto, devo interpretá-lo incorretamente, porque o seguinte não funciona:
#!/usr/bin/env bash
# -*- bash -*-
set -e -o pipefail -o errtrace -o functrace
function boom {
echo "err status: $?"
exit $?
}
trap boom ERR
echo $( made up name )
echo " ! should not be reached ! "
Eu já sei atribuição simples ,, my_var=$(made_up_name)
sairá do script com set -e
(ou seja, errexit).
É -E/-o errtrace
suposto funcionar como o código acima? Ou, provavelmente, eu o interpretei mal?
bash
command-substitution
dgo.a
fonte
fonte
echo $( made up name )
por$( made up name )
produz o comportamento desejado. Eu não tenho uma explicação embora.var=$( pipe )
e$( pipe )
exemplos representariam pontos finais de canal, ao contrário depipe > echo
não. Minha página de manual diz: "1. A falha de qualquer comando individual em um pipeline de comandos múltiplos não deve causar a saída do shell. Somente a falha do pipeline em si deve ser considerada."echo
sempre retorna 0. Isso deve ser levado em consideração na análise ...Respostas:
Nota:
zsh
irá reclamar de "padrões ruins" se você não o configurar para aceitar "comentários embutidos" para a maioria dos exemplos aqui e não executá-los através de um shell proxy como eu fiz anteriormentesh <<-\CMD
.Ok, então, como afirmei nos comentários acima, não sei especificamente sobre o bash
set -E
, mas sei que os shells compatíveis com POSIX fornecem um meio simples de testar um valor, se você desejar:Acima você verá que embora eu utilizado
parameter expansion
para testar${empty?} _test()
aindareturn
é um passe - como é evidenciado na últimaecho
Isso ocorre porque o valor não mata o$( command substitution )
subshell que o contém, mas o seu shell pai -_test
neste momento - continua a camionagem. Eecho
não se importa - é muito bom servir apenas um não\newline; echo
é um teste.Mas considere isso:
Como eu alimentei a
_test()'s
entrada com um parâmetro pré-avaliado noINIT here-document
agora, a_test()
função nem sequer tenta executar. Além do mais, ash
concha aparentemente desiste completamente do fantasma eecho "this doesnt even print"
nem sequer imprime.Provavelmente não é isso que você deseja.
Isso acontece porque o
${var?}
estilo parameter-expansion foi projetado para sairshell
no caso de um parâmetro ausente, funciona assim :Não copio / colo o documento inteiro, mas se você quiser uma falha para um
set but null
valor, use o formulário:Com o
:colon
como acima. Se você deseja que umnull
valor seja bem-sucedido, apenas omita os dois pontos. Você também pode negá-lo e falhar apenas em valores definidos, como mostrarei em um momento.Outra corrida de
_test():
Isso funciona com todos os tipos de testes rápidos, mas, acima, você verá que
_test()
, executado a partir do meio daspipeline
falhas, e de fato seucommand list
subshell contendo falha completamente, pois nenhum dos comandos da função é executado nem aecho
execução a seguir , embora também seja mostrado que ele pode ser facilmente testado porqueecho "now it prints"
agora é impresso.O diabo está nos detalhes, eu acho. No caso acima, o shell que sai não é do script,
_main | logic | pipeline
mas( subshell in which we ${test?} ) ||
é necessário um pouco de sandbox.E pode não ser óbvio, mas se você quiser passar apenas pelo caso oposto, ou apenas
set=
valores, também é bastante simples:O exemplo acima aproveita todas as quatro formas de substituição de parâmetro POSIX e seus vários
:colon null
ounot null
testes. Há mais informações no link acima e aqui está novamente .E acho que deveríamos mostrar nosso
_test
trabalho funcional também, certo? Apenas declaramosempty=something
como parâmetro para nossa função (ou a qualquer momento):Deve-se notar que essa avaliação é independente - não requer teste adicional para falhar. Mais alguns exemplos:
E, finalmente, voltamos à pergunta original: como lidar com erros em um
$(command substitution)
subshell? A verdade é - existem duas maneiras, mas nenhuma é direta. O núcleo do problema é o processo de avaliação do shell - as expansões do shell (inclusive$(command substitution)
) acontecem mais cedo no processo de avaliação do shell do que a execução atual dos comandos do shell - que é quando seus erros podem ser capturados e capturados.O problema que o op experimenta é que, no momento em que o shell atual avalia erros, o
$(command substitution)
subshell já foi substituído - nenhum erro permanece.Então, quais são as duas maneiras? Você faz isso explicitamente dentro do
$(command substitution)
subshell com testes como faria sem ele ou absorve os resultados em uma variável de shell atual e testa seu valor.Método 1:
Método 2:
Isso falhará independentemente do número de variáveis declaradas por linha:
E nosso valor de retorno permanece constante:
AGORA A ARMADILHA:
fonte
echo "abc" "${v1:?}"
isso não parece ser executado (o abc nunca é impresso). E o shell retorna 1. Isso é verdade com ou sem um comando par ("${v1:?}"
diretamente no CLI). Mas, para o script do OP, tudo o que era necessário para acionar a armadilha era colocar sua atribuição de variável contendo uma substituição por um comando inexistente sozinho em uma única linha. Caso contrário, o comportamento do eco é retornar 0 sempre, a menos que seja interrompido como nos testes que você explicou.v=$( madeup )
. Não vejo o que é inseguro com isso. É apenas uma tarefa, a pessoa digitou errado o comando, por exemplov="$(lss)"
. Erros. Sim, você pode verificar com o status de erro do último comando $? - porque é o comando na linha (uma atribuição sem nome de comando) e nada mais - não é um argumento para ecoar. Além disso, aqui está preso pela função como! = 0, para que você obtenha feedback duas vezes. Caso contrário, certamente, como você explica, há uma maneira melhor de fazer isso em ordem dentro de uma estrutura, mas o OP tinha uma única linha: um eco mais sua falha na substituição. Ele estava pensando em eco.No seu script é uma execução de comando (
echo $( made up name )
). No bash, os comandos são delimitados com qualquer um deles ; ou com nova linha . No comando$( made up name )
é considerado como parte do comando. Mesmo se essa parte falhar e retornar com erro, o comando inteiro será executado com êxito, poisecho
não sabemos sobre isso. Como o comando retorna com 0, nenhuma armadilha é acionada.Você precisa colocá-lo em dois comandos, atribuição e eco
fonte
echo $var
não falha -$var
é expandido para esvaziar antes mesmo deecho
realmente dar uma olhada.Isso ocorre devido a um erro no bash. Durante a etapa Substituição de Comando
made
é executado (ou falha em ser encontrado) em um subshell, mas o subshell é "otimizado" de forma a não usar algumas traps do shell pai. Isso foi corrigido na versão 4.4.5 :Com o bash 4.4.5 ou superior, você verá a seguinte saída:
O manipulador de trap foi chamado conforme o esperado e o subshell é encerrado. (
set -e
apenas faz com que o subshell saia, não o pai, de modo que a mensagem "não deve ser alcançada" deve, de fato, ser alcançada.)Uma solução alternativa para versões mais antigas é forçar a criação de um subshell completo e não otimizado:
Os espaços extras são necessários para distinguir da expansão aritmética.
fonte