Como executar o comando e, se falhar, executar outro comando e retornar 1?

3

Estou acostumado com esse estilo em outros idiomas:

do_something || (log_error; return 1) # do something, and if that fails, log the error and then return 1 no matter what, even if the logging fails.

Mas não consigo encontrar um equivalente no bash. O problema é que os parênteses funcionam como uma função com seu próprio escopo, e o retorno 1 não terá o comportamento esperado.

Isto é o que eu tenho até agora, mas não é perfeito:

! do_something && log_error && return 1

O problema com isso é que o! é confuso, e o retorno 1 depende do sucesso do registro.

Este é melhor, mas mais detalhado:

do_something || (log_error; return 1) || return 1

Alguma ideia?

ChocoDeveloper
fonte
o return A instrução é significativa apenas quando dentro de um script ou função - não na linha de comando. Quem você deseja retornar 1 para?
@htor Você está certo, eu sempre uso esse trecho dentro de uma função. Porque às vezes eu preciso incluir o script e outras vezes preciso executá-lo como um script autônomo.
ChocoDeveloper
@ChocoDeveloper: Use return para retornar de uma função de shell, mas continue executando o script de onde foi chamado; usar exit para sair do shell inteiro (ou seja, o script). A menos que você esteja entre parênteses, caso em que você está em um subshell e exit só sai da expressão entre parênteses (subshell).
Gordon Davisson
@GordonDavisson Sim, o problema é que eu tenho o payload do script dentro de parênteses (fora eu só tenho algumas vars e funções que eu sempre uso), para redirecionar o stdout / stderr (na maioria das vezes eu quero ambos) para um arquivo de log, e sair parou esse redirecionamento última vez que tentei. Dessa forma, posso ter cada script lidando com seus próprios logs, em vez de fazer isso do chamador (por exemplo, do cli ou de um arquivo cron).
ChocoDeveloper
1
@ChocoDeveloper: Como cada script (normalmente) é executado como um shell separado, você pode redirecionar com, por exemplo, exec >output.log 2>error.log e isso não vai estragar o script de chamada. Ou você pode usar { payload; } >output.log 2>error.log e o redirecionamento só se aplica ao que está entre parênteses, sem precisar de uma subcamada.
Gordon Davisson

Respostas:

4

Use chaves.

 do_something || { log_error; return 1;}
BatchyX
fonte
1

O status de saída do último comando executado é salvo no $? variável. Então, você poderia fazer algo como o seguinte:

do something
if [[ $? > 0 ]]; then 
   do log_error; exit 1; 
fi

Eu gosto disso por questão de legibilidade. No entanto, o seu é perfeitamente bom, você só precisa substituir && com ;:

! do_something && log_error ; return 1

&& significa "executar o próximo comando SOMENTE SE o anterior saiu com sucesso" enquanto ; significa apenas "executar o próximo comando".

terdon
fonte
No seu segundo exemplo, return 1 é executado em caso de sucesso.
BatchyX
Tanto quanto eu posso dizer, é executado em caso de sucesso e fracasso, conforme desejado. O status de saída de ! ls /etc && echo "FAIL" > ~/log ; return 1 E de ls /etcaaa && echo "FAIL" > ~/log ; return 1 é 1.
terdon