Como fazer com que o bash interrompa a execução de um script em erro de sintaxe?

15

Por segurança, eu gostaria que o bash abortasse a execução de um script se ele encontrar um erro de sintaxe.

Para minha surpresa, não posso conseguir isso. ( set -enão é suficiente.) Exemplo:

#!/bin/bash

# Do exit on any error:
set -e

readonly a=(1 2)

# A syntax error is here:

if (( "${a[#]}" == 2 )); then
    echo ok
else
    echo not ok
fi

echo status $?

echo 'Bad: has not aborted execution on syntax error!'

Resultado (bash-3.2.39 ou bash-3.2.51):

$ ./sh-on-syntax-err
./sh-on-syntax-err: line 10: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Bem, não podemos verificar $?após cada instrução para detectar erros de sintaxe.

(Eu esperava um comportamento tão seguro de uma linguagem de programação sensata ... talvez isso deva ser relatado como um bug / desejo para atacar os desenvolvedores)

Mais experiências

if Não faz diferença.

Removendo if:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
(( "${a[#]}" == 2 ))
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Resultado:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 

Talvez esteja relacionado ao exercício 2 de http://mywiki.wooledge.org/BashFAQ/105 e tenha algo a ver com isso (( )). Mas acho que ainda não é razoável continuar executando após um erro de sintaxe.

Não, (( ))não faz diferença!

Comporta-se mal, mesmo sem o teste aritmético! Apenas um script simples e básico:

#!/bin/bash

set -e # exit on any error
readonly a=(1 2)
# A syntax error is here:
echo "${a[#]}"
echo status $?
echo 'Bad: has not aborted execution on syntax error!'

Resultado:

$ ./sh-on-syntax-err 
./sh-on-syntax-err: line 6: #: syntax error: operand expected (error token is "#")
status 1
Bad: has not aborted execution on syntax error!
$ 
imz - Ivan Zakharyaschev
fonte
set -enão é suficiente porque seu erro de sintaxe está em uma ifinstrução Em qualquer outro lugar deve abortar o script.
jordanm
@ Jordanm Ok, isso pode ser uma explicação porque set -enão funcionou. Mas minha pergunta ainda faz sentido. É possível cancelar qualquer erro de sintaxe?
imz - Ivan Zakharyaschev
@jordanm Removido "if"; não faz diferença (atualizei minha pergunta).
imz - Ivan Zakharyaschev 8/07

Respostas:

9

Agrupar o todo em uma função parece fazer o truque:

#!/bin/bash -e

main () {
readonly a=(1 2)
    # A syntax error is here:
    if (( "${a[#]}" == 2 )); then
        echo ok
    else
        echo not ok
    fi
    echo status $?
    echo 'Bad: has not aborted execution on syntax error!'
}

main "$@"

Resultado:

$ ./sh-on-syntax-err 
$ ./sh-on-syntax-err line 6: #: syntax error: operand expected (error token is "#")
$ 

Embora eu não tenha idéia do porquê - talvez alguém possa explicar?

ahilsend
fonte
2
Agora sua definição de função é analisada e avaliada e falha.
Tripleee
Ótima solução! Aliás, também não aborta todo o programa. Anexei echo 'Bad2: has not aborted the execution after bad main!'como o último ao seu exemplo e a saída é: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: linha 6: #: erro de sintaxe: operando esperado ( token de erro é "#") Bad2: não interrompeu a execução após main ruim! $
imz - Ivan Zakharyaschev
Mas não devemos acrescentar uma linha simplesmente então, devemos colocar tudo dentro de uma função.
imz - Ivan Zakharyaschev
@ tripleeee Sim, parece que a análise da função falha, por isso não está completa, mas todo o programa não é abortado nesse caso (por isso, provavelmente não é o efeito da saída com erro).
imz - Ivan Zakharyaschev
6

Você provavelmente está enganado sobre o significado genuíno de set -e. Uma leitura cuidadosa da saída dos help setshows:

-e  Exit immediately if a command exits with a non-zero status.

O mesmo -eocorre com o status de saída de comandos que é diferente de zero, não com erros de sintaxe no seu script.

Em geral, é considerado uma má prática usar set -e, porque todos os erros (ou seja, todos os retornos diferentes de zero) devem ser manipulados de maneira inteligente pelo script (pense em um script robusto, não nos que ficam loucos depois de inserir um nome de arquivo com um espaço ou que começa com um hypen).

Dependendo do tipo de erro de sintaxe, o script pode nem mesmo ser executado. Não tenho conhecimento suficiente do bash para dizer exatamente que classe de erros de sintaxe (se eles podem ser classificados) pode levar a um aborto imediato do script ou não. Talvez alguns gurus do Bash participem e esclareçam tudo.

Só espero esclarecer a set -eafirmação!

Sobre o seu desejo:

Eu esperava um comportamento tão seguro de uma linguagem de programação sensata ... talvez isso deva ser relatado como um bug / desejo para bash desenvolvedores

A resposta é definitivamente não! como o que você observou ( set -esem responder como você espera) está de fato muito bem documentado.

gniourf_gniourf
fonte
Eu quis dizer que a ausência de tal recurso é um problema. Eu não queria focar set -e- é apenas um pouco perto dos meus objetivos, é por isso que é mencionado e usado aqui. Minha pergunta não é sobre set -e, é sobre a insegurança do bash, se não for possível abortar com erros de sintaxe. Estou procurando uma maneira de fazê-lo sempre abortar com erros de sintaxe.
imz - Ivan Zakharyaschev 8/07
4

Você pode fazer o script verificar-se colocando algo como

bash -n "$0"

próximo ao topo do script - depois, set -emas antes, de qualquer parte significativa do código.

Devo dizer que isso não parece muito robusto, mas se funcionar para você, talvez seja aceitável.

triplo
fonte
Excelente! Ou, sem set -e: bash -n "$0" || exit
Daniel S
0

Primeiro, o (( ))in bash é usado como cálculos aritméticos, para não usar no if ... use o []para isso.

Segundo, o ${a[#]}é estranho e é por isso que está dando erros ... o #não tem nenhum significado de matriz

Eu não sei o que você quer fazer com isso, mas eu suponho que você quer saber o número de campos, assim que você quer ${#a[*]}, em vez

Finalmente, ao comparar números inteiros, -eqrecomenda-se over ==(usado para strings). o ==também funcionará, mas -eqé recomendado.

então você quer:

if [ ${#a[*]} -eq 2 ]; then 
higuita
fonte
4
Isso não é verdade. É comum usar a ((palavra-chave com a ifpalavra - chave. Por exemplo if (( 5 * $b > 53 )),. A menos que você está apontando para portabilidade com conchas mais velhos, [[é geralmente preferível ao longo [.
2
Sim, eu concordo com o @Evan - [[e ((foram projetados especificamente como testes leves para serem usados ​​com "se" etc. - ao contrário [, eles nunca geram um subprocesso para avaliar a condição.
imz - Ivan Zakharyaschev
1
[é um bash embutido. Os reservatórios mais antigos esperam que seja seu próprio programa.