Wie kann ich Bash veranlassen, die Ausführung eines Skripts bei einem Syntaxfehler abzubrechen?

14

Aus Sicherheitsgründen möchte ich, dass Bash die Ausführung eines Skripts abbricht, wenn es auf einen Syntaxfehler stößt.

Zu meiner Überraschung kann ich das nicht erreichen. ( set -eist nicht genug.) Beispiel:

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

Ergebnis (bash-3.2.39 oder 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!
$ 

Nun, wir können nicht $?nach jeder Anweisung prüfen, ob Syntaxfehler vorliegen.

(Ich habe ein solches sicheres Verhalten von einer vernünftigen Programmiersprache erwartet ... vielleicht muss dies als Fehler gemeldet werden / möchte Entwickler verprügeln)

Weitere Experimente

if macht keinen Unterschied.

Entfernen 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!'

Ergebnis:

$ ./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!
$ 

Vielleicht hat es etwas mit Übung 2 von http://mywiki.wooledge.org/BashFAQ/105 zu tun (( )). Aber ich finde es immer noch unvernünftig, die Ausführung nach einem Syntaxfehler fortzusetzen.

Nein, (( ))macht keinen Unterschied!

Es verhält sich auch ohne den Rechentest schlecht! Nur ein einfaches, einfaches Skript:

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

Ergebnis:

$ ./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
quelle
set -eist nicht genug, weil Ihr Syntaxfehler in einer ifAnweisung ist. An jeder anderen Stelle sollte das Skript abgebrochen werden.
Jordan
@jordanm Ok, das kann eine Erklärung sein, warum set -ees nicht funktioniert hat. Aber meine Frage macht immer noch Sinn. Ist es möglich, bei einem Syntaxfehler abzubrechen?
imz - Ivan Zakharyaschev
@jordanm "if" entfernt; macht keinen unterschied (aktualisiert meine frage).
imz - Ivan Zakharyaschev

Antworten:

9

Das Ganze in eine Funktion zu packen, scheint den Trick zu tun:

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

Ergebnis:

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

Obwohl ich keine Ahnung habe, warum - vielleicht kann jemand anderes erklären?

ahilsend
quelle
2
Jetzt wird Ihre Funktionsdefinition analysiert und ausgewertet, und es schlägt fehl.
Tripleee
Schöne lösung! Übrigens wird auch in diesem Fall nicht das gesamte Programm abgebrochen. Ich habe echo 'Bad2: has not aborted the execution after bad main!'als letztes an Ihr Beispiel angehängt und die Ausgabe lautet: $ LC_ALL = C ./sh-on-syntax-err ./sh-on-syntax-err: Zeile 6: #: Syntaxfehler: Operand erwartet ( Fehlertoken ist "#") Bad2: hat die Ausführung nach Bad Main nicht abgebrochen! $
imz - Ivan Zakharyaschev
Aber wir sollten dann nicht einfach eine Zeile anhängen, sondern alles in eine Funktion einfügen.
imz - Ivan Zakharyaschev
@tripleee Ja, es sieht so aus, als ob das Parsen der Funktion fehlschlägt, daher ist sie nicht vollständig, aber in diesem Fall wird das gesamte Programm nicht abgebrochen (es ist also wahrscheinlich nicht die Auswirkung von Exit-on-Error).
imz - Ivan Zakharyaschev
6

Sie sind wahrscheinlich über die wahre Bedeutung von irregeführt set -e. Eine sorgfältige Lektüre der Ausgabe von help setShows:

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

Es geht also -eum den Beendigungsstatus von Befehlen ungleich Null, nicht um Syntaxfehler in Ihrem Skript.

Im Allgemeinen wird die Verwendung als schlechte Praxis angesehen set -e, da alle Fehler (dh alle Rückgaben ungleich Null von Befehlen) vom Skript intelligent behandelt werden sollten (denken Sie an ein robustes Skript, nicht an diejenigen, die nach der Eingabe eines Dateinamens mit einem "wild" werden) Leerzeichen oder das beginnt mit einem Bindestrich).

Je nach Art des Syntaxfehlers wird das Skript möglicherweise überhaupt nicht ausgeführt. Ich kenne mich in Bash nicht gut genug aus, um genau zu sagen , welche Art von Syntaxfehlern (wenn sie nur klassifiziert werden können) zu einem sofortigen Abbruch des Skripts führen könnten oder nicht. Vielleicht machen einige Bash-Gurus mit und klären alles.

Ich hoffe nur, dass ich die set -eAussage geklärt habe !

Über Ihren Wunsch:

Ich habe solch ein sicheres Verhalten von einer vernünftigen Programmiersprache erwartet ... vielleicht muss dies als Fehler / Wunsch gemeldet werden, Entwickler zu verprügeln

Die Antwort ist definitiv nein! Das, was Sie beobachtet haben ( set -ekeine erwartungsgemäße Antwort), ist in der Tat sehr gut dokumentiert.

gniourf_gniourf
quelle
Ich meinte, das Fehlen einer solchen Funktion ist ein Problem. Ich wollte mich nicht darauf konzentrieren set -e- es ist nur ein bisschen nah an meinen Zielen, deshalb wird es hier erwähnt und verwendet. Bei meiner Frage geht es nicht um set -e, es geht um die Unsicherheit von Bash, wenn es nicht möglich ist, bei Systemsteuerfehlern abzubrechen. Ich suche nach einer Möglichkeit, es bei Syntaxfehlern immer abbrechen zu lassen.
imz - Ivan Zakharyaschev
4

Sie könnten das Skript dazu bringen, sich selbst zu überprüfen, indem Sie etwas wie setzen

bash -n "$0"

Nahe dem oberen Rand des Skripts - nach, set -eaber vor einem signifikanten Teil des Codes.

Ich muss sagen, das fühlt sich nicht sehr robust an, aber wenn es für Sie funktioniert, ist es vielleicht akzeptabel.

Tripleee
quelle
Ausgezeichnet! Oder ohne set -e: bash -n "$0" || exit
Daniel S
0

Zuerst die (( )) In-Bash als arithmetische Berechnung verwendet, nicht für die If-Verwendung [].

Zweitens die ${a[#]} seltsam und deshalb werden Fehler ausgegeben ... das #hat keine Array-Bedeutung

Ich weiß nicht, was Sie damit machen wollen, aber ich gehe davon aus, dass Sie die Anzahl der Felder wissen wollen, also wollen Sie ${#a[*]} stattdessen

Beim Vergleichen von Ganzzahlen wird schließlich die Über- -eqOption empfohlen ==(wird für Zeichenfolgen verwendet). das== wird auch funktionieren, wird aber -eqempfohlen.

also du möchtest:

if [ ${#a[*]} -eq 2 ]; then 
Higuita
quelle
4
Das ist nicht wahr. Es ist üblich, das ((Schlüsselwort mit dem ifSchlüsselwort zu verwenden. Zum Beispiel if (( 5 * $b > 53 )). Es sei denn , Sie sind mit dem Ziel für die Portabilität mit älteren Muscheln, [[ist im allgemeinen bevorzugt über [.
2
Ja, ich bin mit @Evan einverstanden [[und ((wurde speziell als Lightweight-Test entwickelt, um mit "if" usw. verwendet [zu werden.
imz - Ivan Zakharyaschev
1
[ist ein bash builtin. Ältere Shells erwarten jedoch, dass es sich um ein eigenes Programm handelt.