1133 Stimmen

Was bedeutet set -e in einem Bash-Skript?

Ich studiere den Inhalt dieser preinst Datei, die das Skript ausführt, bevor dieses Paket aus seiner Debian-Archivdatei (.deb) ausgepackt wird.

Das Skript hat den folgenden Code:

#!/bin/bash
set -e
# Automatisch hinzugefügt von dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName wurde gerade installiert"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# Ende des automatisch hinzugefügten Abschnitts

Meine erste Frage bezieht sich auf die Zeile:

set -e

Ich denke, der Rest des Skripts ist ziemlich einfach: Es prüft, ob der Debian/Ubuntu-Paketmanager eine Installationsoperation ausführt. Wenn ja, wird überprüft, ob meine Anwendung gerade auf dem System installiert wurde. Wenn ja, gibt das Skript die Nachricht "MyApplicationName wurde gerade installiert" aus und endet (return 1 bedeutet, dass es mit einem "Fehler" endet, oder?).

Wenn der Benutzer das Debian/Ubuntu-Paketsystem auffordert, mein Paket zu installieren, löscht das Skript auch zwei Verzeichnisse.

Stimmt das oder habe ich etwas übersehen?

1148voto

Gilles Quenot Punkte 153339

Von help set :

  -e  Sofort beenden, wenn ein Befehl mit einem Nicht-Null-Status beendet wird.

Aber es wird von einigen als schlechte Praxis angesehen (Autoren des Bash-FAQ und irc Freenode #bash FAQ). Es wird empfohlen, Folgendes zu verwenden:

trap 'do_something' ERR

um die Funktion do_something auszuführen, wenn Fehler auftreten.

Siehe http://mywiki.wooledge.org/BashFAQ/105

206voto

Robin Green Punkte 30622

set -e stoppt die Ausführung eines Skripts, wenn ein Befehl oder eine Pipeline einen Fehler aufweist - was dem Standardverhalten der Shell entgegensteht, Fehler in Skripts zu ignorieren. Geben Sie help set in einem Terminal ein, um die Dokumentation für diesen integrierten Befehl zu sehen.

112voto

entpnerd Punkte 8981

Ich habe diesen Beitrag gefunden, während ich versuchte herauszufinden, was der Beendigungsstatus für ein Skript war, das aufgrund von set -e abgebrochen wurde. Die Antwort war für mich nicht offensichtlich, daher diese Antwort. Grundsätzlich beendet set -e die Ausführung eines Befehls (z. B. eines Shell-Skripts) und gibt den Beendigungsstatus des Befehls zurück, der fehlgeschlagen ist (d. h. das innere Skript, nicht das äußere Skript).

Zum Beispiel, nehmen wir an, ich habe das Shell-Skript outer-test.sh:

#!/bin/sh
set -e
./inner-test.sh
exit 62;

Der Code für inner-test.sh lautet:

#!/bin/sh
exit 26;

Wenn ich outer-script.sh von der Befehlszeile ausführe, wird mein äußeres Skript mit dem Beendigungscode des inneren Skripts beendet:

$ ./outer-test.sh
$ echo $?
26

73voto

kenorb Punkte 134883

Laut bash - Das Set Builtin Handbuch, wenn -e/errexit gesetzt ist, beendet sich die Shell sofort, wenn eine Pipeline, die aus einem einzelnen einfachen Befehl, einer Liste oder einem zusammengesetzten Befehl besteht, einen Status ungleich Null zurückgibt.

Standardmäßig ist der Exit-Status einer Pipeline der Exit-Status des letzten Befehls in der Pipeline, es sei denn, die pipefail Option ist aktiviert (standardmäßig deaktiviert).

Wenn ja, ist der Rückgabestatus der Pipeline der des letzten (rechtesten) Befehls, der mit einem Status ungleich Null ausgeführt wird, oder Null, wenn alle Befehle erfolgreich beendet werden.

Wenn Sie beim Beenden etwas ausführen möchten, versuchen Sie, trap zu definieren, zum Beispiel:

trap onexit EXIT

wo onexit Ihre Funktion ist, die beim Beenden etwas ausführen soll, wie zum Beispiel das Drucken des einfachen Stack-Traces:

onexit(){ while caller $((n++)); do :; done; }

Es gibt eine ähnliche Option -E/errtrace, die stattdessen bei ERR-Fällen triggern würde, z.B.:

trap onerr ERR

Beispiele

Beispiel mit Null-Status:

$ true; echo $?
0

Beispiel mit Nicht-Null-Status:

$ false; echo $?
1

Beispiele mit negiertem Status:

$ ! false; echo $?
0
$ false || true; echo $?
0

Test mit deaktivierter pipefail:

$ bash -c 'set +o pipefail -e; true | true | true; echo Erfolg'; echo $?
Erfolg
0
$ bash -c 'set +o pipefail -e; false | false | true; echo Erfolg'; echo $?
Erfolg
0
$ bash -c 'set +o pipefail -e; true | true | false; echo Erfolg'; echo $?
1

Test mit aktivierter pipefail:

$ bash -c 'set -o pipefail -e; true | false | true; echo Erfolg'; echo $?
1

34voto

tripleee Punkte 155951

Dies ist eine alte Frage, aber keiner der Antworten hier diskutiert die Verwendung von set -e aka set -o errexit in Debian-Paket-Handling-Skripten. Die Verwendung dieser Option ist in diesen Skripten obligatorisch gemäß der Debian-Richtlinie; offensichtlich soll jede Möglichkeit eines unbehandelten Fehlerzustands vermieden werden.

Was dies in der Praxis bedeutet, ist, dass Sie unter welchen Bedingungen die von Ihnen ausgeführten Befehle einen Fehler zurückgeben können, verstehen müssen und jeden dieser Fehler explizit behandeln müssen.

Gemeinsame Fallstricke sind z. B. diff (gibt einen Fehler zurück, wenn es einen Unterschied gibt) und grep (gibt einen Fehler zurück, wenn es keine Übereinstimmung gibt). Sie können die Fehler mit expliziter Behandlung vermeiden:

diff das dies ||
  echo "$0: Es gab einen Unterschied" >&2
grep katze essen ||
  echo "$0: keine Katze im Essen" >&2

(Beachten Sie auch, wie wir darauf achten, den Namen des aktuellen Skripts in der Nachricht zu erwähnen und Diagnosemeldungen an den Standardfehlerausgang anstelle des Standardausgangs zu schreiben.)

Wenn keine explizite Bearbeitung wirklich notwendig oder nützlich ist, tun Sie explizit nichts:

diff das dies || true
grep katze essen || :

(Die Verwendung des No-Op-Befehls : der Shell ist etwas obskur, aber ziemlich häufig gesehen.)

Nur zur Wiederholung,

etwas || anderer

ist eine Abkürzung für

if something; then
    : nichts
else
    other
fi

d. h. wir sagen ausdrücklich, dass other ausgeführt werden soll, wenn und nur wenn something fehlschlägt. Die ausführliche if-Anweisung (und andere Shell-Flusssteuerungsanweisungen wie while, until) ist auch ein gültiger Weg, um einen Fehler zu behandeln (tatsächlich könnten shell-Skripte mit set -e keine Flusssteuerungsanweisungen enthalten, wenn dies nicht der Fall wäre!)

Und auch nur zur Verdeutlichung, in Abwesenheit eines Handlers wie diesem würde set -e das gesamte Skript sofort mit einem Fehler fehlschlagen lassen, wenn diff einen Unterschied fände oder wenn grep keine Übereinstimmung fände.

Andererseits produzieren einige Befehle keinen Fehlerausgangsstatus, wenn Sie möchten, dass sie dies tun. Häufig problematische Befehle sind z. B. find (der Ausgangsstatus spiegelt nicht wider, ob tatsächlich Dateien gefunden wurden) und sed (der Ausgangsstatus verrät nicht, ob das Skript Eingaben erhalten hat oder tatsächlich erfolgreich Befehle ausgeführt hat). Eine einfache Absicherung in einigen Szenarien ist, an einen Befehl zu pipen, der schreit, wenn keine Ausgabe vorhanden ist:

find dinge | grep .
sed -e 's/o/ich/' sachen | grep ^

Es sollte angemerkt werden, dass der Ausgangsstatus einer Pipeline der Ausgangsstatus des letzten Befehls in dieser Pipeline ist. Daher maskieren die obigen Befehle tatsächlich vollständig den Status von find und sedgrep schließlich erfolgreich war.

(Bash hat natürlich set -o pipefail; aber Debian-Paket-Skripte können keine Bash-Funktionen verwenden. Die Richtlinie schreibt eindeutig die Verwendung von POSIX sh für diese Skripte vor, obwohl dies nicht immer der Fall war.)

In vielen Situationen ist dies etwas, worauf man bei defensiver Programmierung gesondert achten muss. Manchmal muss man z. B. durch eine temporäre Datei gehen, damit man sehen kann, ob der Befehl, der diese Ausgabe erzeugt hat, erfolgreich beendet wurde, auch wenn Idiom und Bequemlichkeit Sie eigentlich dazu bringen würden, eine Shell-Pipeline zu verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X