Como parar o script bash quando uma condição falha?

11

Aqui, mostra-se o uso de ||e &&em uma única linha para concatenar a execução de comandos: Como posso verificar se há erros do apt-get em um script bash?

Estou tentando parar a execução de um script se uma determinada condição falhar,

por exemplo

false || echo "Obvious error because its false on left" && exit

Aqui ele imprime Obvious error because its false on the lefte sai do console, o que eu queria.

true || echo "This shouldn't print" && exit

Aqui, não há echo print, mas o exitcomando também é executado, ou seja, o console está fechado, o exitcomando não deve ser executado porque o comando echo à direita não foi executado? Ou, por padrão, é uma declaração considerada falsa no lado esquerdo de um &&operador?

Edit: Eu deveria ter mencionado isso antes, meu objetivo era repetir o erro e sair se não estivesse claro. Para o meu caso específico de detectar erros ao agrupar condições usando && e ||, a resposta @ bodhi.zazen resolve o problema.

A resposta @takatakatek torna mais claro o controle de fluxo e os links do guia do bash são excelentes

A resposta do @muru tem uma boa explicação de por que não usar set -e, se você deseja que mensagens de erro personalizadas sejam exibidas com alternativas de uso de perl e trap, que eu acho que é uma maneira mais robusta e me vejo usando-a a partir do meu segundo script do bash!

Kosol
fonte
Tanto quanto eu posso dizer, ele termina quando o script é feito.
Panther

Respostas:

10

Você provavelmente quer (como apontado por steeldriver)

true || { echo "This shouldn't print" && exit; }

Caso contrário, seu script está lendo uma condicional por vez

a ou be, em seguida, saia

Eu acho que você quer a ou (be sair)?

Pantera
fonte
4
Na verdade, você deve usar { ... ; }(como @steeldriver sugeriu), ou a exitúnica sai do subshell, e o shell pai continua executando o script.
Gordon Davisson
14

O problema é que || e && pula apenas uma sub-rotina subsequente de uma cadeia de comandos quando a condição falha. Se você escrever uma estrutura de bloco completa, faz todo o sentido. O que você escreveu se torna o seguinte:

if ! true ; then
   echo "This shouldn't print"
else
   exit
fi

O que você quer é isso:

if ! true ; then
   echo "This shouldn't print"
   exit
fi

Quando você usa os operadores condicionais de atalho do Bash, é melhor evitar misturá-los. A lógica é muito mais fácil de entender enquanto você a escreve, e seus leitores apreciarão seu bom estilo.

takatakatek
fonte
2
Sim, na minha opinião isso é muito mais como o condicionamento em bash deve ser escrito
derHugo
@takatakatek Isso deixa mais claro por que falha no meu caso. Concordo que a lógica é clara nesta, mas não é o motivo para agrupar condições usando && e || é evitar expressá-las usando instruções condicionais?
kosol
@kosol Sim, && e || combine duas coisas: Testando o status de saída do comando anterior e especificando um único comando (ou grupo de comandos) a ser executado (para &&) ou ignorado (para ||). Eles podem ser úteis para casos muito simples, mas não podem substituir if ... then ... elif ... else ... fiblocos totalmente expressos . Eu suspeito que você pode não estar ciente de que o Bash não tem tipos booleanos verdadeiros vistos em idiomas mais sofisticados. Se o status de saída de um comando for 0 (zero), o Bash tratará isso como verdadeiro / bem-sucedido . Se o status de saída for diferente de zero, o Bash tratará isso como falso / falha .
takatakatek
7

A maneira mais simples de falhar em erro é usar a -eopção do bash ( set -eou /bin/bash -eno shebang). No entanto, isso não facilita o envio de mensagens de erro personalizadas. Existem algumas expressões idiomáticas que podem simplificar:

die à la Perl

Perl tem um muito conveniente diecomando que imprime uma mensagem para stderr e levanta uma exceção, o que geralmente leva à terminação script. Você pode emular isso:

die () {
  ret=$?
  print "%s\n" "$@" >&2
  exit "$ret"
}

false || die "Obvious error because its false on left"
true || die "This shouldn't print"

trap ERR

O trap <command>comando pode ser usado para executar um comando quando um sinal é recebido, ou ao sair do script ( trap ... EXIT), ou quando um comando retorna um erro ( trap ... ERR). Então, algo como:

trap 'ret=$?; printf "%s\n" "$ERR_MSG" >&2; exit "$ret"' ERR

Então:

ERR_MSG="Obvious error because its false here"
false
ERR_MSG="This shouldn't print"
true

Em ambos os casos, sugiro usar pelo menos exit 1para indicar que o script falhou . Uma planície exitusará o status de saída do comando anterior, que nesses casos seriam os comandos echoou printf, que normalmente seriam bem-sucedidos. Salvar o status de saída do comando com falha, como fiz aqui, seria bom de se ter.

muru
fonte
6

Você pode experimentar um pouco ao iniciar o script com

#!/bin/bash -e

O -e garante que o script saia no momento em que algo retornar falso. Uma falha específica pode ser evitada comsomething || true

agregate1166877
fonte