Como fazer o bash abortar a execução de um script no erro de sintaxe?

14

Para estar no lado seguro, eu gostaria de abortar a execução de um script se ele encontrar um erro de sintaxe.

Para minha surpresa, não posso conseguir isso. ( set -e nã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 capturar erros de sintaxe.

(Eu esperava esse comportamento seguro de uma linguagem de programação sensata ... talvez isso deva ser relatado como um bug / desejo de prejudicar 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 do link e tenha algo a ver com (( )) . Mas acho que ainda não é razoável continuar executando depois um erro de sintaxe.

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

Ele se comporta 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!
$ 
    
por imz -- Ivan Zakharyaschev 08.07.2013 / 18:04

4 respostas

9

Envolvendo 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 ideia do porquê - talvez alguém possa explicar?

    
por 08.07.2013 / 18:42
6

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

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

Portanto, -e é sobre o status de saída de comandos sendo diferentes de zero, não sobre erros de sintaxe em seu script.

Em geral, considera-se má prática usar set -e , porque todos os erros (ou seja, todos os retornos diferentes de zero dos comandos) devem ser inteligentemente manipulados pelo script (pense em scripts robustos, não aqueles você digita 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 ser executado. Eu não tenho conhecimento suficiente no bash para dizer exatamente qual classe de erros de sintaxe (se apenas eles podem ser classificados) pode levar a um aborto imediato do script ou não. Talvez alguns gurus Bash se juntem e esclareçam tudo.

Espero apenas ter esclarecido a declaração set -e !

Sobre o seu desejo:

I expected such safe behavior from a sensible programming language... perhaps this must be reported as a bug/wish to bash developers

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

    
por 08.07.2013 / 20:48
4

Você pode fazer o script verificar a si mesmo colocando algo como

bash -n "$0"

perto da parte superior do script - depois de set -e , mas antes de qualquer parte significativa do código.

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

    
por 08.07.2013 / 19:02
0

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

Em segundo lugar, 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 suponho que você queira saber o número de campos, então você quer ${#a[*]}

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

assim você quer:

if [ ${#a[*]} -eq 2 ]; then 
    
por 08.07.2013 / 19:18