El estado de salida de Bash no se detecta a pesar de que set -e y / o trap están activos

8

¿Alguien puede explicar el comportamiento bash / set -e en el fragmento de código a continuación?

#!/bin/bash

# Comment if you want to test the trap only
set -e -o pipefail -u -E

# Comment if you want to test the set -e only
trap "echo ERROR CAUGHT; exit 12" ERR

function reproduce() {
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
}

reproduce

# The script is expected to trigger the trap and/or activate the set -e. In both cases it should stop and exit here on error.

status_code=$?
echo "STATUS ${status_code}"
if [[ "${status_code}" != "0" ]];then
    echo "FIXME: why was status code not caught by set -e ?"
    echo "Executing false to prove set -e is still active"
    false
    # If the following is not executed then it proves -e is still active
    echo "set -e not active !!!!!"
    exit 2
fi

Esto es lo que se obtiene al ejecutarlo:

$ bash reproduce.sh
reproduce.sh: line 8: 1109962735 - hello=12272 + 1: attempted assignment to non-variable (error token is "=12272 + 1")
STATUS 1
FIXME: why was status code it not caught by set -e ?
Executing false to prove set -e is still active
ERROR CAUGHT

Comprobando el código de salida

$ echo $?
1

Versión bash

bash --version
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)

Reproducido también con

GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)

Notas adicionales, relacionadas con comentarios (gracias a todas las sugerencias de todos modos):

  • La trampa de comentarios no cambia el comportamiento extraño observado
  • Eliminar set -e para mantener la trampa solo activa la trampa.
raphael.glon
fuente
1
No me combinaría set -econ trap. trapse invoca en caso de error y se invoca "echo ERROR CAUGHT". Tengo la impresión de que traptiene mayor prioridad que set -e. También de acuerdo con las preguntas frecuentes de Bash, creo que no set -ese recomienda , consulte mywiki.wooledge.org/BashFAQ/105 .
stephanmg
comentar trampa no cambia nada
raphael.glon
Simplemente usaría el trapmecanismo en su lugar, por ejemplo trap "exit 2" ERR. También para mí, su script imprime "STATUS 0" solamente. Parece que la trampa ERR no es heredada por las funciones de shell, ¿esto ayuda set -o errtrace? De lo contrario, vea mi enlace anterior sobre por qué debe evitar set -een primer lugar.
stephanmg

Respuestas:

3

Simplifiquemoslo; La cantidad mínima de código requerida para reproducir el problema con el que está lidiando es

set -e
: $((+)) # arithmetic expansion error
echo survived

Según el estándar, esto nunca debería imprimirse survived, dice que un shell POSIX que se ejecuta de forma no interactiva saldrá inmediatamente ante un error de expansión . Pero aparentemente Bash no lo cree así. Aunque esta diferencia no está documentada explícitamente en la página del manual, en la descripción del modo POSIX dice

  1. Los shells no interactivos salen si un error de sintaxis en una expansión aritmética da como resultado una expresión no válida.

Podemos decir que esto significa que en su modo de funcionamiento predeterminado, una sesión Bash no interactiva no se cierra con dicho error, pero como se dio cuenta, tampoco activa el mecanismo errexit ni la trampa ERR. En cambio, asigna un valor distinto de cero a los $?movimientos.

Para superar esto y obtener el comportamiento esperado, debe definir reproducelo siguiente

function reproduce() (
    # Trigger arithmetic error on purpose
    a=$((1109962735 - hello=12272 + 1))
)

De esta forma, el error de expansión tendrá lugar en una subshell y hará que salga con un estado distinto de cero, por lo tanto, errexit y trap podrán capturarlo.


A pedido de dash-o, aquí hay una versión que establece ael entorno de ejecución actual cuando la expresión es válida

function reproduce() {
    if : $((expression)); then
        a=$((expression))
    fi
}
oguz ismail
fuente
2

En la superficie, parece que bash no activará la trampa en varios errores SYNTAX. Solo cuando se ejecuta un comando (externo, incorporado) (y devuelve un valor distinto de cero), la trampa ERR se activará.

Desde la página del manual:

Si un sigspec es ERR, el comando arg se ejecuta cada vez que una tubería (que puede consistir en un solo comando simple), una lista o un comando compuesto devuelve un estado de salida distinto de cero, sujeto a las siguientes condiciones ...

La trampa ERR solo se aplica a PIPELINE . Si bash identifica un error de sintaxis, aborta antes de ejecutar la canalización, por lo tanto, NO hay trampa. Aunque la documentación para '-e' especifica la misma condición ( if a pipeline ... exit with non-zero status), el comportamiento observado es diferente.

Si intenta otras expansiones, por ejemplo, el comando expansion-trap se activa, ya que hay una ejecución de canalización:

  • a = $ (comandos incorrectos)
  • a = $ ([)

Si se usa, pruebe varios errores de sintaxis en la expansión aritmética, la trampa no se activa, no hubo canalización.

  • a = $ ((2+))
  • a = $ ((2 @))

Además, otro error de sintaxis de bash no activa la trampa: (), [[ ]].

No pude encontrar una solución que no requiera grandes cambios en el script de origen. ¿Se puede presentar una solicitud de error / función con el equipo de bash?

guión
fuente
2
Dato curioso: ejecutarlo en una subshell ( a=$((1109962735 - hello=12272 + 1)) )o ( reproduce )ejecuta la trampa.
KamilCuk
Desafortunadamente, cuando la expresión es válida, ano se establecerá.
dash-o