¿Cómo hacer que bash anule la ejecución de un script en error de sintaxis?

15

Para estar seguro, me gustaría que bash aborte la ejecución de un script si encuentra un error de sintaxis.

Para mi sorpresa, no puedo lograr esto. ( set -eno es suficiente) Ejemplo:

#!/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 o 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!
$ 

Bueno, no podemos verificar $?después de cada declaración para detectar errores de sintaxis.

(Esperaba un comportamiento tan seguro de un lenguaje de programación sensible ... tal vez esto debe ser reportado como un error / deseo de criticar a los desarrolladores)

Más experimentos

if No hace diferencia.

Removiendo 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!
$ 

Quizás, está relacionado con el ejercicio 2 de http://mywiki.wooledge.org/BashFAQ/105 y tiene algo que ver (( )). Pero todavía me parece irrazonable continuar ejecutando después de un error de sintaxis.

No, (( ))no hay diferencia!

¡Se comporta mal incluso sin la prueba aritmética! Solo un script simple y 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
fuente
set -eno es suficiente porque su error de sintaxis está en una ifdeclaración. En cualquier otro lugar debería abortar el script.
jordanm
@jordanm Ok, esto puede ser una explicación de por qué set -eno ha funcionado. Pero mi pregunta aún tiene sentido. ¿Es posible abortar en cualquier error de sintaxis?
imz - Ivan Zakharyaschev
@jordanm Eliminado "if"; no hace ninguna diferencia (actualicé mi pregunta).
imz - Ivan Zakharyaschev

Respuestas:

9

Ajustar el todo en una función parece hacer el truco:

#!/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 "#")
$ 

Aunque no tengo idea de por qué, ¿tal vez alguien más pueda explicarlo?

ahilsend
fuente
2
Ahora su definición de función se analiza y evalúa, y falla.
tripleee
Buena solución! Por cierto, no aborta todo el programa en este caso, también. He agregado echo 'Bad2: has not aborted the execution after bad main!'como último a su ejemplo, y el resultado es: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: línea 6: #: error de sintaxis: operando esperado ( el token de error es "#") Malo2: ¡no ha abortado la ejecución después de un mal principal! $
imz - Ivan Zakharyaschev
Pero no deberíamos agregar una línea simplemente entonces, deberíamos poner todo dentro de una función.
imz - Ivan Zakharyaschev
@tripleee Sí, parece que el análisis de la función falla, por lo que no está completo, pero el programa completo no se cancela en este caso (por lo que probablemente no sea el efecto de la salida en caso de error).
imz - Ivan Zakharyaschev
6

Probablemente esté engañando sobre el significado genuino de set -e. Una lectura cuidadosa de la salida de help setespectáculos:

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

Entonces, -ese trata del estado de salida de los comandos que no son cero, no de los errores de sintaxis en su script.

En general, se considera una mala práctica usar set -e, porque todos los errores (es decir, todos los retornos distintos de cero de los comandos) deben ser manejados de manera inteligente por el script (piense en un script robusto, no los que se vuelven locos después de ingresar un nombre de archivo con un espacio o que comienza con una exageración).

Dependiendo del tipo de error de sintaxis, es posible que el script ni siquiera se ejecute. No tengo suficiente conocimiento en bash para decir exactamente qué clase de errores de sintaxis (si solo se pueden clasificar) podría conducir a un aborto inmediato del script o no. Quizás algunos gurús de Bash se unan y aclaren todo.

¡Solo espero haber aclarado la set -edeclaración!

Sobre su deseo:

Esperaba un comportamiento tan seguro de un lenguaje de programación sensible ... tal vez esto debe ser reportado como un error / deseo de criticar a los desarrolladores

La respuesta es definitivamente no! ya que lo que has observado ( set -esin responder como esperas) está de hecho muy bien documentado.

gniourf_gniourf
fuente
Quise decir que la ausencia de tal característica es un problema. No quería centrarme set -e, está un poco cerca de mis objetivos, es por eso que se menciona y se usa aquí. Mi pregunta no set -ees acerca de la inseguridad de bash si no se puede abortar en errores de sintaxis. Estoy buscando una manera de hacerlo siempre abortar en errores de sintaxis.
imz - Ivan Zakharyaschev
4

Puede hacer que el script se verifique por sí mismo poniendo algo como

bash -n "$0"

cerca de la parte superior de la secuencia de comandos, después set -epero antes de cualquier fragmento de código significativo.

Debo decir que esto no se siente muy robusto, pero si funciona para usted, tal vez sea aceptable.

tripleee
fuente
¡Excelente! O sin set -e: bash -n "$0" || exit
Daniel S
0

Primero el (( )) in bash se usa como cálculos aritméticos, no para usar en el if ... use the []para eso.

En segundo lugar, el ${a[#]} es extraño y es por eso que está dando errores ... #no tiene ningún significado de matriz

No sé qué quieres hacer con esto, pero supongo que quieres saber la cantidad de campos, así que quieres ${#a[*]} en su lugar

Finalmente, cuando se comparan enteros, -eqse recomienda sobre ==(usado para cadenas). el ==también funcionará, pero -eqse recomienda.

Entonces quieres:

if [ ${#a[*]} -eq 2 ]; then 
higuita
fuente
44
Eso no es cierto. Es común usar la ((palabra clave con la ifpalabra clave. Por ejemplo, if (( 5 * $b > 53 )). A menos que esté buscando la portabilidad con proyectiles más antiguos, generalmente[[ es preferible hacerlo [.
2
Sí, estoy de acuerdo con @Evan, [[y ((fueron diseñados específicamente como pruebas livianas para usar con "si", etc., a diferencia de esto [, nunca generan un subproceso para evaluar la condición.
imz - Ivan Zakharyaschev
1
[es un bash incorporado. Sin embargo, los proyectiles más antiguos esperan que sea su propio programa.