Existe algo semelhante ao pipefail para vários comandos, como uma instrução 'try', mas dentro do bash. Eu gostaria de fazer algo assim:
echo "trying stuff"
try {
command1
command2
command3
}
E a qualquer momento, se algum comando falhar, saia e repita o erro desse comando. Eu não quero ter que fazer algo como:
command1
if [ $? -ne 0 ]; then
echo "command1 borked it"
fi
command2
if [ $? -ne 0 ]; then
echo "command2 borked it"
fi
E assim por diante ... ou algo assim:
pipefail -o
command1 "arg1" "arg2" | command2 "arg1" "arg2" | command3
Porque os argumentos de cada comando que acredito (corrija-me se estiver errado) interferirão um no outro. Esses dois métodos me parecem terrivelmente longos e desagradáveis, então estou aqui pedindo um método mais eficiente.
set -euo pipefail
.set -e
é uma ideia horrível . Veja os exercícios no BashFAQ # 105 discutindo apenas alguns dos casos inesperados apresentados, e / ou a comparação mostrando incompatibilidades entre diferentes implementações de shells (e versões de shell ') em in-ulm.de/~mascheck/various/set -e .Respostas:
Você pode escrever uma função que inicia e testa o comando para você. Suponha
command1
ecommand2
são variáveis de ambiente que foram configuradas para um comando.fonte
$*
, ele falhará se algum argumento tiver espaços; use em"$@"
vez disso. Da mesma forma, coloque$1
dentro das aspas noecho
comando.test
pois esse é um comando interno.ls
. Se você chamarls foo
e receber uma mensagem de erro do formulário,ls: foo: No such file or directory\n
entenderá o problema. Se, em vez disso, vocêls: foo: No such file or directory\nerror with ls\n
se distrair com informações supérfluas. Nesse caso, é fácil argumentar que a superfluidade é trivial, mas cresce rapidamente. Mensagens de erro concisas são importantes. Mais importante, porém, esse tipo de wrapper incentiva gravadores demais a omitir completamente boas mensagens de erro.O que você quer dizer com "desistir e repetir o erro"? Se você quer dizer que deseja que o script seja finalizado assim que qualquer comando falhar, basta
no início do script (mas observe o aviso abaixo). Não se preocupe em repetir a mensagem de erro: deixe o comando com falha lidar com isso. Em outras palavras, se você fizer:
e command2 falhar, ao imprimir uma mensagem de erro no stderr, parece que você conseguiu o que deseja. (A menos que eu interprete mal o que você quer!)
Como corolário, qualquer comando que você escreve deve se comportar bem: ele deve relatar erros ao stderr em vez de stdout (o código de exemplo na pergunta imprime erros no stdout) e deve sair com um status diferente de zero quando falhar.
No entanto, não considero mais isso uma boa prática.
set -e
mudou sua semântica com diferentes versões do bash e, embora funcione bem para um script simples, há tantos casos extremos que é essencialmente inutilizável. (Considere coisas como:set -e; foo() { false; echo should not print; } ; foo && echo ok
A semântica aqui é um tanto razoável, mas se você refatorar o código em uma função que dependia da configuração da opção para terminar mais cedo, poderá facilmente ser mordido.) IMO, é melhor escrever:ou
fonte
trap some_func 0
será executadosome_func
na saída)|| exit
explicitamente após cada comando.Eu tenho um conjunto de funções de script que eu uso extensivamente no meu sistema Red Hat. Eles usam as funções do sistema de
/etc/init.d/functions
para imprimir indicadores de status verde[ OK ]
e vermelho[FAILED]
.Opcionalmente, você pode definir a
$LOG_STEPS
variável como um nome de arquivo de log se desejar registrar quais comandos falharão.Uso
Resultado
Código
fonte
Pelo que vale, uma maneira mais curta de escrever código para verificar se cada comando é bem-sucedido é:
Ainda é entediante, mas pelo menos é legível.
fonte
command1 &> /dev/null || echo "command1 borked it"
command1 || (echo command1 borked it ; exit)
Uma alternativa é simplesmente unir os comandos,
&&
para que o primeiro a falhar impeça a execução do restante:Essa não é a sintaxe solicitada na pergunta, mas é um padrão comum para o caso de uso que você descreve. Em geral, os comandos devem ser responsáveis pelas falhas de impressão, para que você não precise fazer isso manualmente (talvez com um
-q
sinalizador para silenciar os erros quando não os desejar). Se você tem a capacidade de modificar esses comandos, eu os edito para gritar com falha, em vez de envolvê-los em outra coisa que o faça.Observe também que você não precisa fazer:
Você pode simplesmente dizer:
E quando você fazer necessidade de verificar os códigos de retorno usar um contexto aritmética em vez de
[ ... -ne
:fonte
Em vez de criar funções de corredor ou usar
set -e
, use umtrap
:A armadilha ainda tem acesso ao número da linha e à linha de comando do comando que a acionou. As variáveis são
$BASH_LINENO
e$BASH_COMMAND
.fonte
trap - ERR
para desativar a armadilha no final do "bloco".Pessoalmente, prefiro usar uma abordagem leve, como pode ser visto aqui ;
Exemplo de uso:
fonte
fonte
$*
, ele falhará se algum argumento tiver espaços neles; use em"$@"
vez disso. (Embora $ * esteja ok noecho
comando).Desenvolvi uma implementação try & catch quase perfeita no bash, que permite escrever código como:
Você pode até aninhar os blocos try-catch dentro de si!
O código faz parte do meu boilerplate / framework do bash . Além disso, amplia a idéia de tentar e capturar coisas como manipulação de erros com backtrace e exceções (além de outros recursos interessantes).
Aqui está o código responsável apenas pelo try & catch:
Sinta-se livre para usar, usar e contribuir - é no GitHub .
fonte
Desculpe por não poder comentar a primeira resposta. Mas você deve usar uma nova instância para executar o comando: cmd_output = $ ($ @)
fonte
Para usuários de casca de peixe que tropeçam nesse segmento.
Let
foo
Ser uma função que não "retorna" (eco) um valor, mas define o código de saída como de costume.Para evitar a verificação
$status
após chamar a função, você pode:E se for muito longo para caber em uma linha:
fonte
Quando uso
ssh
, preciso diferenciar os problemas causados por problemas de conexão e os códigos de erro do comando remoto no modoerrexit
(set -e
). Eu uso a seguinte função:fonte
Você pode usar a incrível solução de @ john-kugelman encontrada acima em sistemas não-RedHat, comentando esta linha em seu código:
Em seguida, cole o código abaixo no final. Divulgação completa: Esta é apenas uma cópia e colagem direta dos bits relevantes do arquivo acima mencionado, extraídos do Centos 7.
Testado no MacOS e Ubuntu 18.04.
fonte
Verificando o status de maneira funcional
Uso:
Resultado
fonte