Was macht 'set -e' und warum wird es als gefährlich eingestuft?

147

Diese Frage ist in einem Quiz vor dem Interview aufgetaucht und macht mich verrückt. Kann jemand darauf antworten und mich beruhigen? Das Quiz bezieht sich nicht auf eine bestimmte Shell, die Jobbeschreibung bezieht sich jedoch auf eine Unix-sa. wieder ist die frage einfach ...

Was macht 'set -e' und warum wird es als gefährlich eingestuft?

eifrig
quelle
Hier ist ein verwandter Thread: stackoverflow.com/questions/64786/error-handling-in-bash/…
Stefan Lasiewski

Antworten:

154

set -e Bewirkt, dass die Shell beendet wird, wenn ein Unterbefehl oder eine Pipeline einen Status ungleich Null zurückgibt.

Die Antwort, nach der der Interviewer wahrscheinlich gesucht hat, lautet:

Es wäre gefährlich, beim Erstellen von init.d-Skripten "set -e" zu verwenden:

Von http://www.debian.org/doc/debian-policy/ch-opersys.html 9.3.2 -

Achten Sie darauf, set -e in init.d-Skripten zu verwenden. Das Schreiben korrekter init.d-Skripte erfordert das Akzeptieren verschiedener Fehler-Exit-Status, wenn Daemons bereits ausgeführt oder gestoppt werden, ohne das init.d-Skript abzubrechen, und gemeinsame init.d-Funktionsbibliotheken können nicht sicher mit set -e aufgerufen werden. Bei init.d-Skripten ist es häufig einfacher, set -e nicht zu verwenden und stattdessen das Ergebnis jedes Befehls separat zu prüfen.

Dies ist vom Standpunkt eines Interviewers aus eine berechtigte Frage, da sie die Kenntnisse eines Kandidaten in Bezug auf Scripting und Automatisierung auf Serverebene misst

Reich
quelle
guter Punkt. es würde den Boot-Prozess wegen eines technisch möglicherweise fehlerhaften Vorgangs anhalten, aber nicht dazu führen, dass das gesamte Skript angehalten wird
Matt,
Obwohl ich mit stackoverflow.com/questions/78497/… einverstanden bin , finde ich, dass dieser Stil gut zu Bash für kurze Initialisierungsprüfungen und Protokollierung oder zum Patchen anderer Umgebungsaffen passt, bevor die Ziel-Workload ausgeführt wird. Wir verwenden dieselbe Richtlinie für unser Docker-Image Init-Skripte.
Joshperry
33

Von bash(1):

          -e      Exit immediately if a pipeline (which may consist  of  a
                  single  simple command),  a subshell command enclosed in
                  parentheses, or one of the commands executed as part  of
                  a  command  list  enclosed  by braces (see SHELL GRAMMAR
                  above) exits with a non-zero status.  The shell does not
                  exit  if  the  command that fails is part of the command
                  list immediately following a  while  or  until  keyword,
                  part  of  the  test  following  the  if or elif reserved
                  words, part of any command executed in a && or  ││  list
                  except  the  command  following  the final && or ││, any
                  command in a pipeline but the last, or if the  command’s
                  return  value  is being inverted with !.  A trap on ERR,
                  if set, is executed before the shell exits.  This option
                  applies to the shell environment and each subshell envi-
                  ronment separately (see  COMMAND  EXECUTION  ENVIRONMENT
                  above), and may cause subshells to exit before executing
                  all the commands in the subshell.

Leider bin ich nicht kreativ genug, um darüber nachzudenken, warum es gefährlich sein könnte, außer "der Rest des Skripts wird nicht ausgeführt" oder "es könnte möglicherweise echte Probleme maskieren".

Ignacio Vazquez-Abrams
quelle
1
real / other problems maskieren, yeah, ich schätze, ich konnte sehen, dass ... ich
mich
Es gibt die Manpage für set! Ich lande immer bei builtin(1). Vielen Dank.
Jared Beck
woher soll ich wissen, dass die dokumentation dafür setzu finden ist man 1 bash?? und wie könnte jemand die -eOption für das setSchlüsselwort in einer Handbuchseite finden, die so enorm groß ist? Sie können hier nicht einfach /-esuchen
phil294
@Blauhirn usinghelp set
phil294
19

Es ist zu beachten, dass set -efür verschiedene Abschnitte eines Skripts aktiviert und deaktiviert werden kann. Es muss nicht für die Ausführung des gesamten Skripts aktiviert sein. Es könnte sogar bedingt aktiviert werden. Das heißt, ich benutze es nie, da ich meine eigene Fehlerbehandlung mache (oder nicht).

some code
set -e     # same as set -o errexit
more code  # exit on non-zero status
set +e     # same as set +o errexit
more code  # no exit on non-zero status

Bemerkenswert ist auch der Abschnitt auf der Bash-Manpage des trapBefehls, in dem auch die set -eFunktionsweise unter bestimmten Umständen beschrieben wird.

Die ERR-Trap wird nicht ausgeführt, wenn der fehlgeschlagene Befehl Teil der Befehlsliste ist, die unmittelbar auf ein Schlüsselwort while oder until folgt, Teil des Tests in einer if-Anweisung, Teil eines Befehls, der in einer &&- oder ⎪⎪-Liste ausgeführt wird, oder wenn der Befehl ist Rückgabewert wird über! invertiert. Dies sind die gleichen Bedingungen, die von der Option errexit eingehalten werden.

Es gibt also einige Bedingungen, unter denen ein Nicht-Null-Status kein Verlassen verursacht.

Ich denke, die Gefahr besteht darin, nicht zu verstehen, wann etwas set -epassiert und wann nicht, und mich unter einer ungültigen Annahme fälschlicherweise darauf zu verlassen.

Siehe auch BashFAQ / 105. Warum macht set -e (oder set -o errexit oder trap ERR) nicht das, was ich erwartet hatte?

Dennis Williamson
quelle
Diese Antwort weicht zwar ein wenig von der Frage ab, ist aber genau das, wonach ich suche: Deaktivieren Sie sie nur für einen kleinen Teil eines Skripts!
Stephan Bijzitter
13

Denken Sie daran, dies ist ein Quiz für ein Vorstellungsgespräch. Die Fragen wurden möglicherweise vom aktuellen Personal geschrieben und sind möglicherweise falsch. Das ist nicht unbedingt schlecht, und jeder macht Fehler, und Interviewfragen sitzen oft in einer dunklen Ecke ohne Überprüfung und kommen nur während eines Interviews heraus.

Es ist durchaus möglich, dass 'set -e' nichts tut, was wir als "gefährlich" betrachten würden. Aber der Autor dieser Frage könnte irrtümlich glauben, dass 'set -e' gefährlich ist, aufgrund ihrer eigenen Ignoranz oder Voreingenommenheit. Vielleicht haben sie ein fehlerhaftes Shell-Skript geschrieben, das schrecklich bombardiert wurde, und sie dachten fälschlicherweise, dass 'set -e' schuld ist, obwohl sie es versäumt haben, eine ordnungsgemäße Fehlerprüfung zu schreiben.

Ich habe in den letzten 2 Jahren an wahrscheinlich 40 Vorstellungsgesprächen teilgenommen, und die Interviewer stellen manchmal Fragen, die falsch sind, oder haben Antworten, die falsch sind.

Oder vielleicht ist es eine Trickfrage, die lahm wäre, aber nicht ganz unerwartet.

Oder vielleicht ist dies eine gute Erklärung: http://www.mail-archive.com/[email protected]/msg473314.html

Stefan Lasiewski
quelle
1
+1 Ich sitze im selben Boot und viele der 'technischen' Interviews, mit denen ich zu tun hatte, waren zumindest leicht fehlgeleitet.
cpbills
1
Beeindruckend. Guter Fund auf der Debian-Liste. +1 für die Unkenntnis von Interviewfragen. Ich erinnere mich, einmal eine Netback-Antwort ausgesprochen zu haben, da ich zu 100% sicher war, dass ich Recht hatte. Sie sagten nein. Ich ging nach Hause und googelte es, ich hatte recht.
egorgry
9

set -e Weist bash in einem Skript an, den Vorgang zu beenden, wenn ein Wert ungleich Null zurückgegeben wird.

Ich konnte sehen, wie ärgerlich und fehlerhaft das wäre, nicht sicher, was gefährlich ist, es sei denn, Sie hatten Berechtigungen für etwas geöffnet und bevor Sie sie wieder einschränken konnten, starb Ihr Skript.

cpbills
quelle
Das ist interessant. Ich dachte nicht an Berechtigungen, die in einem Skript geöffnet wurden, das vorzeitig stirbt, aber ich konnte sehen, dass dies als gefährlich eingestuft wurde. Der lustige Teil dieses Quiz ist, dass Sie keine Referenzmaterialien wie man oder google verwenden können und wenn Sie nicht vollständig antworten können, beantworten Sie es überhaupt nicht.
egorgry
6
Das ist nur albern, ich würde diesen Arbeitgeber überdenken ... Scherz. Es ist gut, eine solide Wissensbasis zu haben, aber die Hälfte der IT-Arbeit weiß, wo und wo die Informationen zu finden sind ... hoffentlich waren sie klug genug, dies bei der Bewertung von Bewerbern zu berücksichtigen. Nebenbei bemerkt, ich sehe / no / use set -e, für mich spricht es von Faulheit, die Fehlerprüfung in Ihren Skripten zu ignorieren. Denken Sie also daran, auch bei diesem Arbeitgeber, wenn er sie verwendet viel, wie sehen ihre Skripte aus, die Sie zwangsläufig pflegen müssen ...
cpbills
ausgezeichneter Punkt @cpbills. Ich sehe auch keine Verwendung für set -e in einem Skript und es ist wahrscheinlich, warum es mich so sehr verblüfft hat. Ich würde gerne alle Fragen posten, da einige wirklich gut sind und andere wirklich verrückt und zufällig wie diese.
egorgry
Ich habe es in vielen System-Cron-Jobs gesehen ...
Andrew
8

set -eBeendet das Skript, wenn ein Exit-Code ungleich Null auftritt, außer unter bestimmten Bedingungen. Um die Gefahren seines Gebrauchs in wenigen Worten zusammenzufassen: Es verhält sich nicht so, wie die Leute denken, dass es es tut.

Meiner Meinung nach sollte es als gefährlicher Hack angesehen werden, der nur noch aus Kompatibilitätsgründen existiert. Die set -eAnweisung wandelt die Shell nicht von einer Sprache, die Fehlercodes verwendet, in eine Sprache um, die einen ausnahmeartigen Steuerungsfluss verwendet. Sie versucht lediglich, dieses Verhalten nur schlecht zu emulieren.

Greg Wooledge hat viel zu den Gefahren von set -e:

Im zweiten Link gibt es verschiedene Beispiele für das unintuitive und unvorhersehbare Verhalten von set -e.

Einige Beispiele für das nicht intuitive Verhalten von set -e(einige stammen aus dem obigen Wiki-Link):

  • set -e
    x = 0
    lass x ++
    echo "x ist $ x"
    Das Obige bewirkt, dass das Shell-Skript vorzeitig beendet wird, da let x++0 zurückgegeben wird, was vom letSchlüsselwort als falscher Wert behandelt und in einen Exit-Code ungleich Null umgewandelt wird. set -ebemerkt dies und beendet das Skript unbemerkt.
  • set -e
    [-d / opt / foo] && echo "Warnung: foo ist bereits installiert. Wird überschrieben." > & 2
    echo "Installiere foo ..."
    

    Das oben beschriebene Verfahren funktioniert wie erwartet und gibt eine Warnung aus, falls diese /opt/foobereits vorhanden ist.

    set -e
    check_previous_install () {
        [-d / opt / foo] && echo "Warnung: foo ist bereits installiert. Wird überschrieben." > & 2
    }
    
    check_previous_install
    echo "Installiere foo ..."
    

    Das Obige endet, obwohl der einzige Unterschied darin besteht, dass eine einzelne Zeile in eine Funktion umgestaltet wurde, wenn /opt/fooes nicht existiert. Dies liegt daran, dass die Tatsache, dass es ursprünglich funktionierte, eine besondere Ausnahme von seinem set -eVerhalten darstellt. Wenn der a && bWert ungleich Null ist, wird er von ignoriert set -e. Da es sich jedoch um eine Funktion handelt, entspricht der Exit-Code der Funktion dem Exit-Code dieses Befehls, und die Funktion, die einen Wert ungleich Null zurückgibt, beendet das Skript im Hintergrund.

  • set -e
    IFS = $ '\ n' read -d '' -r -a config_vars <config
    

    Das obige liest das Array config_varsaus der Datei config. Wie der Autor vielleicht beabsichtigt, wird es mit einem Fehler beendet, wenn configes fehlt. Da der Autor möglicherweise nicht beabsichtigt, wird er stillschweigend beendet, wenn er confignicht in einer neuen Zeile endet. Wenn set -ehier nicht verwendet config_varswürde , würden alle Zeilen der Datei enthalten sein, unabhängig davon, ob sie in einer neuen Zeile endete oder nicht.

    Benutzer von Sublime Text (und anderen Texteditoren, die falsch mit Zeilenumbrüchen umgehen) sollten aufpassen.

  • set -e
    
    should_audit_user () {
        lokale Gruppe groups = "$ (groups" $ 1 ")"
        für Gruppe in $ Gruppen; machen
            if ["$ group" = audit]; dann gebe 0 zurück; fi
        getan
        return 1
    }
    
    if should_audit_user "$ user"; dann
        Logger 'Blah'
    fi
    

    Der Autor kann davon ausgehen, dass $userder groupsBefehl fehlschlägt , wenn der Benutzer aus irgendeinem Grund nicht vorhanden ist, und das Skript beendet wird, anstatt den Benutzer eine Aufgabe ungeprüft ausführen zu lassen. In diesem Fall wird die set -eKündigung jedoch niemals wirksam. Wenn $userdas Skript aus irgendeinem Grund nicht gefunden werden kann, gibt die should_audit_userFunktion nur falsche Daten zurück, als wäre sie set -enicht in Kraft.

    Dies gilt für alle Funktionen, die aus dem Bedingungsteil einer ifAnweisung aufgerufen werden , unabhängig davon, wie tief sie verschachtelt sind, unabhängig davon, wo sie definiert sind, auch wenn Sie set -esie erneut ausführen . Wenn Sie ifzu einem beliebigen Zeitpunkt verwenden, wird der Effekt vollständig deaktiviert, set -ebis der Bedingungsblock vollständig ausgeführt ist. Wenn der Autor diese Fallstricke nicht kennt oder seinen gesamten Aufrufstapel in allen möglichen Situationen, in denen eine Funktion aufgerufen werden kann, nicht kennt, schreibt er fehlerhaften Code, und das falsche Sicherheitsgefühl, das von bereitgestellt wird, set -ewird zumindest teilweise aufgehoben Schuld.

    Selbst wenn der Autor sich dieser Fallstricke voll bewusst ist, besteht die Problemumgehung darin, Code auf die gleiche Weise zu schreiben, wie man es ohne ihn schreiben würde set -e, wodurch dieser Switch weniger als nutzlos wird. Der Autor muss nicht nur den Code für die manuelle Fehlerbehandlung so schreiben, als ob er set -enicht in Kraft wäre, sondern das Vorhandensein von hat ihn set -emöglicherweise in die Irre geführt, dass er dies nicht tun muss.

Einige weitere Nachteile von set -e:

  • Es fördert schlampigen Code. Fehlerbehandler werden völlig vergessen, in der Hoffnung, dass alles, was fehlgeschlagen ist, den Fehler auf eine vernünftige Weise meldet. Bei Beispielen wie let x++oben ist dies jedoch nicht der Fall. Wenn das Skript unerwartet abstürzt, geschieht dies normalerweise im Hintergrund, was das Debuggen behindert. Wenn das Skript nicht stirbt und Sie es erwartet haben (siehe vorherigen Aufzählungspunkt), haben Sie einen subtileren und heimtückischeren Fehler in Ihren Händen.
  • Es führt die Menschen in ein falsches Sicherheitsgefühl. Sehen Sie noch einmal den ifAufzählungspunkt.
  • Die Stellen, an denen die Shell endet, sind zwischen Shells oder Shell-Versionen nicht konsistent. Es ist möglich, versehentlich ein Skript zu schreiben, das sich in einer älteren Version von bash anders verhält, set -eweil zwischen diesen Versionen Optimierungen vorgenommen wurden.

set -eist ein umstrittenes Problem, und einige Leute, die sich der Probleme bewusst sind, empfehlen, dagegen vorzugehen, während andere nur empfehlen, vorsichtig zu sein, während es aktiv ist, um die Fallstricke zu kennen. Es gibt viele Anfänger in der Shell-Skripterstellung, die set -ebei allen Skripten Abhilfe schaffen , aber im wirklichen Leben funktioniert das nicht so.

set -e ist kein Ersatz für Bildung.

Score_Under
quelle
2

Ich würde sagen, es ist gefährlich, weil du den Fluss deines Skripts nicht mehr kontrollierst. Das Skript kann beendet werden, solange einer der vom Skript aufgerufenen Befehle einen Wert ungleich Null zurückgibt. Alles, was Sie tun müssen, ist, etwas zu tun, das das Verhalten oder die Ausgabe einer der Komponenten ändert, und Sie müssen das Hauptskript beenden. Es mag eher ein Stilproblem sein, aber es hat definitiv Konsequenzen. Was ist, wenn das Hauptskript von Ihnen ein Flag setzen soll und nicht, weil es vorzeitig beendet wurde? Sie würden am Ende den Rest des Systems bemängeln, wenn davon ausgegangen wird, dass das Flag vorhanden sein sollte, oder wenn Sie mit einem unerwarteten Standardwert oder einem alten Wert arbeiten.

Marcin
quelle
Stimme dieser Antwort voll und ganz zu. Es ist nicht von Natur aus gefährlich, aber es ist eine Gelegenheit, unerwartet vorzeitig auszusteigen.
pboin
1
Was mich daran erinnerte, war ein C ++ - Profi von mir, der immer Punkte aus meinen Aufgaben zog, weil er keinen einzigen Einstiegspunkt / einzigen Ausstiegspunkt in einem Programm hatte. Ich dachte, es sei eine reine Grundsatzsache, aber dieses Set-e-Geschäft demonstriert definitiv, wie und warum Dinge völlig außer Kontrolle geraten können. Letztendlich geht es bei Programmen um Kontrolle und Determinismus, und bei vorzeitiger Beendigung gibt man beides auf.
Marcin
0

Stellen Sie sich als konkreteres Beispiel für @ Marcins Antwort, die mich persönlich gebissen hat, vor, dass rm foo/bar.txtirgendwo in Ihrem Skript eine Zeile vorkommt. Normalerweise keine große Sache, wenn foo/bar.txtes sie nicht gibt. Mit dem set -ejetzigen Zeitpunkt wird Ihr Skript dort jedoch vorzeitig beendet! Hoppla.

Suan
quelle