it-swarm.com.de

Was bedeutet set -e in einem Bash-Skript?

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

Das Skript hat den folgenden Code:

#!/bin/bash
set -e
# Automatically added by dh_installinit
if [ "$1" = install ]; then
   if [ -d /usr/share/MyApplicationName ]; then
     echo "MyApplicationName is just installed"
     return 1
   fi
   rm -Rf $HOME/.config/nautilus-actions/nautilus-actions.conf
   rm -Rf $HOME/.local/share/file-manager/actions/*
fi
# End automatically added section

Meine erste Frage betrifft die Zeile:

set -e

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

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

Ist das richtig oder fehle ich etwas?

580
AndreaNobili

Aus help set:

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

Einige (bash FAQ und irc freenode #bash FAQ Autoren) halten dies jedoch für eine schlechte Praxis. Es wird empfohlen, Folgendes zu verwenden:

trap 'do_something' ERR

do_something ausführen, wenn Fehler auftreten.

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

665
Gilles Quenot

set -e beendet die Ausführung eines Skripts, wenn ein Befehl oder eine Pipeline einen Fehler aufweist. Dies ist das Gegenteil des standardmäßigen Shell-Verhaltens, bei dem Fehler in Skripts ignoriert werden. Geben Sie help set in ein Terminal ein, um die Dokumentation für diesen integrierten Befehl anzuzeigen.

81
Robin Green

Laut bash - The Set Builtin Handbuch wird Shell sofort beendet, wenn _-e_/errexit gesetzt ist, wenn eine Pipeline aus einem einzelnen einfachen Befehl besteht , eine Liste oder ein zusammengesetzter Befehl gibt einen Nicht-Null-Status zurück.

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

In diesem Fall wird der Rückgabestatus der Pipeline des letzten (am weitesten rechts stehenden) Befehls mit einem Status ungleich Null oder Null, wenn alle Befehle erfolgreich beendet wurden, zurückgegeben.

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

_trap onexit EXIT
_

wobei onexit Ihre Funktion ist, um beim Beenden etwas zu tun, wie unten das einfache Stack-Trace :

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

Es gibt eine ähnliche Option -E_/errtrace , die stattdessen auf ERR abfängt, z.

_trap onerr ERR
_

Beispiele

Beispiel für einen Nullstatus:

_$ true; echo $?
0
_

Beispiel für einen Status ungleich Null:

_$ false; echo $?
1
_

Beispiele für das Negieren des Status:

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

Test mit deaktiviertem pipefail:

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

Test mit aktiviertem pipefail:

_$ bash -c 'set -o pipefail -e; true | false | true; echo success'; echo $?
1
_
48
kenorb

Ich habe diese Frage beim Googeln gefunden, um herauszufinden, wie der Beendigungsstatus eines Skripts lautete, das aufgrund eines set -e abgebrochen wurde. Die Antwort erschien mir nicht offensichtlich; daher diese Antwort. Grundsätzlich bricht set -e die Ausführung eines Befehls (z. B. eines Shell-Skripts) ab und gibt den Beendigungsstatuscode des fehlgeschlagenen Befehls zurück (d. H. Das innere Skript, nicht das äußere Skript.

Angenommen, ich habe ein 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 über die Befehlszeile starte, wird mein äußeres Skript mit dem Exit-Code des inneren Skripts beendet:

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

Ich glaube, die Absicht ist, dass das fragliche Skript schnell versagt.

Um dies selbst zu testen, geben Sie einfach set -e an einer Bash-Eingabeaufforderung ein. Versuchen Sie nun, ls auszuführen. Sie erhalten eine Verzeichnisliste. Geben Sie nun lsd ein. Dieser Befehl wird nicht erkannt und gibt einen Fehlercode zurück. Daher wird Ihre Bash-Eingabeaufforderung geschlossen (aufgrund von set -e).

Um dies im Kontext eines "Skripts" zu verstehen, verwenden Sie dieses einfache Skript:

#!/bin/bash 
# set -e

lsd 

ls

Wenn Sie es so ausführen, wie es ist, erhalten Sie die Verzeichnisliste aus ls in der letzten Zeile. Wenn Sie den Kommentar zu set -e entfernen und erneut ausführen, wird die Verzeichnisliste nicht angezeigt, da die Bash-Verarbeitung beendet wird, sobald der Fehler von lsd auftritt.

8
Script 1: without setting -e
#!/bin/bash
decho "hi"
echo "hello"
This will throw error in decho and program continuous to next line

Script 2: With setting -e
#!/bin/bash
set -e
decho "hi" 
echo "hello"
# Up to decho "hi" Shell will process and program exit, it will not proceed further
5
Manikandan Raj

Dies ist eine alte Frage, aber in keiner der Antworten hier wird die Verwendung von set -e aka set -o errexit in Debian-Pakethandhabungsskripten erörtert. Die Verwendung dieser Option ist obligatorisch in diesen Skripten gemäß Debian-Richtlinie; Die Absicht ist offenbar, die Möglichkeit eines nicht behandelten Fehlerzustands zu vermeiden.

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

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

diff this that ||
  echo "$0: there was a difference" >&2
grep cat food ||
  echo "$0: no cat in the food" >&2

(Beachten Sie auch, wie wir darauf achten, den Namen des aktuellen Skripts in die Nachricht aufzunehmen und Diagnosemeldungen in den Standardfehler anstatt in die Standardausgabe zu schreiben.)

Wenn keine explizite Behandlung wirklich notwendig oder sinnvoll ist, tun Sie explizit nichts:

diff this that || true
grep cat food || :

(Die Verwendung des Befehls : no-op der Shell ist etwas unklar, wird aber häufig gesehen.)

Nur um es noch einmal zu wiederholen,

something || other

ist eine Abkürzung für

if something; then
    : nothing
else
    other
fi

d.h. wir sagen ausdrücklich, dass other genau dann ausgeführt werden sollte, wenn something fehlschlägt. Die Langschrift if (und andere Shell-Flusssteuerungsanweisungen wie while, until) ist auch eine gültige Methode, um einen Fehler zu behandeln (wenn dies nicht der Fall wäre, müssen Shell-Skripte mit set -e könnte niemals Anweisungen zur Flusskontrolle enthalten!)

Nur um es deutlich auszudrücken, würde set -e in Abwesenheit eines Handlers wie diesem dazu führen, dass das gesamte Skript sofort mit einem Fehler fehlschlägt, wenn diff einen Unterschied feststellt oder grep habe keine Übereinstimmung gefunden.

Auf der anderen Seite erzeugen einige Befehle keinen Fehler-Exit-Status, wenn Sie dies wünschen. Häufig problematische Befehle sind find (der Beendigungsstatus gibt nicht an, ob Dateien tatsächlich gefunden wurden) und sed (der Beendigungsstatus gibt nicht an, ob das Skript Eingaben empfangen oder Befehle erfolgreich ausgeführt hat). Ein einfacher Schutz in einigen Szenarien ist, zu einem Befehl zu leiten, der schreit, wenn es keine Ausgabe gibt:

find things | grep .
sed -e 's/o/me/' stuff | grep ^

Es ist zu beachten, dass der Beendigungsstatus einer Pipeline der Beendigungsstatus des letzten Befehls in dieser Pipeline ist. Die obigen Befehle maskieren also den Status von find und sed vollständig und geben nur an, ob grep erfolgreich war.

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

In vielen Situationen ist dies beim defensiven Codieren besonders zu beachten. Manchmal muss man z. Durchsuchen Sie eine temporäre Datei, damit Sie sehen können, ob der Befehl, der diese Ausgabe erzeugt hat, erfolgreich abgeschlossen wurde, auch wenn Sie sonst aufgrund von Redewendung und Bequemlichkeit angewiesen würden, eine Shell-Pipeline zu verwenden.

5
tripleee