Bash-Exit-Status wird nicht erfasst, obwohl Set -e und / oder Trap aktiv sind

8

Kann jemand das Bash / Set-e-Verhalten im folgenden Code-Snippet erklären?

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

Folgendes wird bei der Ausführung erhalten:

$ 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

Überprüfen des Exit-Codes

$ echo $?
1

Bash-Version

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

Auch reproduziert mit

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

Zusätzliche Hinweise zu Kommentaren (trotzdem danke an alle Vorschläge):

  • Das Kommentieren der Falle ändert nichts an dem beobachteten seltsamen Verhalten
  • Wenn Sie set -e entfernen, um nur die Falle zu behalten, wird die Falle ebenfalls ausgelöst
raphael.glon
quelle
1
Ich würde nicht kombinieren set -emit trap. trapwird bei einem Fehler aufgerufen und "echo ERROR CAUGHT" wird aufgerufen. Ich habe den Eindruck, dass trapVorrang hat als set -e. Laut Bash-FAQ, die ich für set -eentmutigt halte , überprüfen Sie bitte mywiki.wooledge.org/BashFAQ/105 .
stephanmg
Das Kommentieren der Falle ändert nichts
raphael.glon
Ich würde trapstattdessen einfach den Mechanismus verwenden, z trap "exit 2" ERR. Auch für mich druckt Ihr Skript nur "STATUS 0". Die ERR-Falle wird anscheinend nicht von Shell-Funktionen geerbt. Hilft dies : set -o errtrace? Ansonsten siehe meinen Link oben, warum du das überhaupt vermeiden solltest set -e.
stephanmg

Antworten:

3

Vereinfachen wir es; Die Mindestmenge an Code, die zum Reproduzieren des Problems erforderlich ist, ist

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

Laut Standard sollte dies niemals gedruckt survivedwerden. Eine nicht interaktiv laufende POSIX-Shell soll bei einem Erweiterungsfehler sofort beendet werden . Aber anscheinend glaubt Bash das nicht. Obwohl dieser Unterschied nicht explizit in der man - Seite dokumentiert, in Beschreibung des POSIX - Modus es sagt

  1. Nicht interaktive Shells werden beendet, wenn ein Syntaxfehler in einer arithmetischen Erweiterung zu einem ungültigen Ausdruck führt.

Wir können sagen, dass dies bedeutet, dass eine nicht interaktive Bash-Sitzung im Standardbetriebsmodus bei einem solchen Fehler nicht beendet wird, aber wie Sie erkannt haben, löst sie weder einen Errexit-Mechanismus noch eine ERR-Falle aus. Stattdessen weist es einem Weitergehen einen Wert ungleich Null zu $?.

Um dies zu überwinden und das erwartete Verhalten zu erhalten, sollten Sie reproduceFolgendes definieren

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

Auf diese Weise tritt der Erweiterungsfehler in einer Unterschale auf und bewirkt, dass sie mit einem Status ungleich Null beendet wird, sodass Errexit und Trap ihn abfangen können.


Auf Anforderung von dash-o finden Sie hier eine Version, die afür die aktuelle Ausführungsumgebung festgelegt wird, wenn der Ausdruck gültig ist

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

An der Oberfläche sieht es so aus, als würde Bash die Falle bei verschiedenen SYNTAX-Fehlern nicht auslösen. Nur wenn ein Befehl (extern, integriert) ausgeführt wird (und nicht Null zurückgibt), wird der ERR-Trap ausgelöst.

Von der Manpage:

Wenn eine Sigspec ERR ist, wird der Befehl arg immer dann ausgeführt, wenn eine Pipeline (die aus einem einzelnen einfachen Befehl bestehen kann), eine Liste oder ein zusammengesetzter Befehl unter den folgenden Bedingungen einen Exit-Status ungleich Null zurückgibt ...

Die ERR-Falle gilt nur für PIPELINE . Wenn bash einen Syntaxfehler feststellt, wird dieser vor dem Ausführen der Pipeline abgebrochen, daher KEIN Trap. Obwohl die Dokumentation für '-e' dieselbe Bedingung ( if a pipeline ... exit with non-zero status) angibt , ist das beobachtete Verhalten unterschiedlich.

Wenn Sie andere Erweiterungen ausprobieren - z. B. Befehlserweiterung - wird Trap ausgelöst, da die Pipeline ausgeführt wird:

  • a = $ (schlechte Befehle)
  • a = $ ([)

Wenn Sie versuchen, verschiedene Syntaxfehler in der arithmetischen Erweiterung zu versuchen, Trap nicht ausgelöst - es gab keine Pipeline.

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

Auch andere Bash-Syntaxfehler lösen den Trap nicht aus: (), [[ ]].

Ich konnte keine Lösung finden, die keine umfangreichen Änderungen am Quellenskript erfordert. Kann eine Bash / Feature-Anfrage beim Bash-Team eingereicht werden?

dash-o
quelle
2
Unterhaltsame Tatsache: Ausführen in einer Subshell ( a=$((1109962735 - hello=12272 + 1)) )oder Ausführen ( reproduce )der Falle.
KamilCuk
Wenn der Ausdruck gültig ist, awird er leider nicht festgelegt.
Dash-o