Was ist Ihre Lieblingsmethode, um Fehler in Bash zu behandeln? Das beste Beispiel für den Umgang mit Fehlern, das ich im Internet gefunden habe, wurde von William Shotts, Jr., geschrieben http://www.linuxcommand.org geschrieben .
Er schlägt vor, die folgende Funktion zur Fehlerbehandlung in Bash zu verwenden:
#!/bin/bash
# A slicker error handling routine
# I put a variable in my scripts named PROGNAME which
# holds the name of the program being run. You can get this
# value from the first item on the command line ($0).
# Reference: This was copied from <http://www.linuxcommand.org/wss0150.php>
PROGNAME=$(basename $0)
function error_exit
{
# ----------------------------------------------------------------
# Function for exit due to fatal program error
# Accepts 1 argument:
# string containing descriptive error message
# ----------------------------------------------------------------
echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
exit 1
}
# Example call of the error_exit function. Note the inclusion
# of the LINENO environment variable. It contains the current
# line number.
echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."
Haben Sie eine bessere Fehlerbehandlungsroutine, die Sie in Bash-Skripten verwenden?
Antworten:
Benutze eine Falle!
... dann, wann immer Sie eine temporäre Datei erstellen:
und
$temp_foo
wird beim Beenden gelöscht und die aktuelle Zeilennummer wird gedruckt. (gibtset -e
Ihnen ebenfalls ein Exit-on-Error-Verhalten, obwohl es mit schwerwiegenden Einschränkungen verbunden ist und die Vorhersagbarkeit und Portabilität des Codes schwächt).Sie können den Trap entweder
error
für Sie aufrufen lassen (in diesem Fall wird der Standard-Exit-Code 1 und keine Nachricht verwendet) oder ihn selbst aufrufen und explizite Werte angeben. zum Beispiel:wird mit Status 2 beendet und gibt eine explizite Nachricht.
quelle
Das ist eine gute Lösung. Ich wollte nur hinzufügen
als rudimentärer Fehlermechanismus. Das Skript wird sofort gestoppt, wenn ein einfacher Befehl fehlschlägt. Ich denke, dies hätte das Standardverhalten sein sollen: Da solche Fehler fast immer etwas Unerwartetes bedeuten, ist es nicht wirklich „vernünftig“, die folgenden Befehle weiterhin auszuführen.
quelle
set -e
ist nicht ohne Fallstricke: Siehe mywiki.wooledge.org/BashFAQ/105 für mehrere.set -o pipefail
set -e
das Nutzen-Kosten-Verhältnis hoch ist.set -e
mich selbst, aber einige der anderen Stammgäste in irc.freenode.org # bash raten (ziemlich stark) davon ab. Zumindest sollten die fraglichen Fallstricke gut verstanden werden.Das Lesen aller Antworten auf dieser Seite hat mich sehr inspiriert.
Also, hier ist mein Hinweis:
Dateiinhalt: lib.trap.sh
Anwendungsbeispiel:
Dateiinhalt: trap-test.sh
Laufen:
Ausgabe:
Wie Sie dem folgenden Screenshot entnehmen können, ist die Ausgabe farbig und die Fehlermeldung wird in der verwendeten Sprache angezeigt.
quelle
test ${#g_libs[@]} == 0
ist nicht POSIX-kompatibel (POSIX-Test unterstützt=
Zeichenfolgenvergleiche oder-eq
numerische Vergleiche, aber nicht==
zu vergessen das Fehlen von Arrays in POSIX), und wenn Sie nicht versuchen, POSIX-kompatibel zu sein, warum in der Welt benutzt dutest
überhaupt eher als einen mathematischen Kontext?(( ${#g_libs[@]} == 0 ))
ist schließlich leichter zu lesen.case "$(uname)" in Darwin ) stderr_log="${TMPDIR}stderr.log";; Linux ) stderr_log="/dev/shm/stderr.log";; * ) stderr_log="/dev/shm/stderr.log" ;; esac
Eine äquivalente Alternative zu "set -e" ist
Es macht die Bedeutung der Flagge etwas klarer als nur "-e".
Zufällige Hinzufügung: Um das Flag vorübergehend zu deaktivieren und zum Standard zurückzukehren (fortlaufende Ausführung unabhängig von den Exit-Codes), verwenden Sie einfach
Dies schließt eine ordnungsgemäße Fehlerbehandlung aus, die in anderen Antworten erwähnt wird, ist jedoch schnell und effektiv (genau wie Bash).
quelle
$(foo)
auf einer bloßen Linie und nicht nurfoo
ist normalerweise das Falsche. Warum es fördern, indem man es als Beispiel gibt?Inspiriert von den hier vorgestellten Ideen habe ich eine lesbare und bequeme Methode entwickelt, um Fehler in Bash-Skripten in meinem Bash-Boilerplate-Projekt zu behandeln .
Durch einfache die Bibliothek Sourcing, erhalten Sie die folgenden aus dem Kasten (dh es wird die Ausführung bei jedem Fehler zu stoppen, als bei Verwendung
set -e
durch einetrap
aufERR
und einig Bash-fu ):Es gibt einige zusätzliche Funktionen, die bei der Behandlung von Fehlern helfen, z. B. try and catch oder das Schlüsselwort throw , mit dem Sie die Ausführung an einem Punkt unterbrechen können, an dem die Rückverfolgung angezeigt wird. Wenn das Terminal dies unterstützt, spuckt es außerdem Powerline-Emojis aus, färbt Teile der Ausgabe für eine gute Lesbarkeit und unterstreicht die Methode, die die Ausnahme im Kontext der Codezeile verursacht hat.
Der Nachteil ist - es ist nicht portabel -, dass der Code in Bash funktioniert, wahrscheinlich nur> = 4 (aber ich würde mir vorstellen, dass er mit etwas Aufwand auf Bash 3 portiert werden könnte).
Der Code ist zur besseren Handhabung in mehrere Dateien unterteilt, aber ich wurde von der Backtrace-Idee aus der obigen Antwort von Luca Borrione inspiriert .
Weitere Informationen oder einen Blick auf die Quelle finden Sie unter GitHub:
https://github.com/niieani/bash-oo-framework#error-handling-with-exceptions-and-throw
quelle
Ich bevorzuge etwas, das wirklich einfach anzurufen ist. Also benutze ich etwas, das etwas kompliziert aussieht, aber einfach zu bedienen ist. Normalerweise kopiere ich einfach den folgenden Code und füge ihn in meine Skripte ein. Eine Erklärung folgt dem Code.
Normalerweise rufe ich die Bereinigungsfunktion neben der Funktion error_exit auf, aber dies variiert von Skript zu Skript, sodass ich es weggelassen habe. Die Fallen erfassen die gemeinsamen Abschlusssignale und sorgen dafür, dass alles aufgeräumt wird. Der Alias ist das, was die wahre Magie bewirkt. Ich überprüfe gerne alles auf Fehler. Im Allgemeinen rufe ich Programme in einem "Wenn!" Typanweisung. Durch Subtrahieren von 1 von der Zeilennummer teilt mir der Alias mit, wo der Fehler aufgetreten ist. Es ist auch kinderleicht anzurufen und ziemlich idiotensicher. Unten finden Sie ein Beispiel (ersetzen Sie einfach / bin / false durch das, was Sie aufrufen möchten).
quelle
$LINENO - 1
. Ohne es richtig anzeigen.false || die "hello death"
Eine weitere Überlegung ist der zurückzugebende Exit-Code. Nur "
1
" ist ziemlich Standard, obwohl es eine Handvoll reservierter Exit-Codes gibt, die bash selbst verwendet , und dieselbe Seite argumentiert, dass benutzerdefinierte Codes im Bereich von 64 bis 113 liegen sollten, um den C / C ++ - Standards zu entsprechen.Sie können auch den Bitvektoransatz in Betracht ziehen,
mount
der für seine Exit-Codes verwendet wird:OR
Wenn Sie die Codes zusammenfügen, kann Ihr Skript mehrere gleichzeitige Fehler anzeigen.quelle
Ich verwende den folgenden Trap-Code, mit dem Fehler auch über Pipes und 'Time'-Befehle verfolgt werden können
quelle
function
Schlüsselwort ist unentgeltlich POSIX-inkompatibel. Überlegen Sie, ob Sie Ihre Erklärung nurerror() {
ohne Nein abgeben möchtenfunction
.${$?}
sollte nur sein$?
, oder${?}
wenn Sie darauf bestehen, unnötige Zahnspangen zu verwenden; Das Innere$
ist falsch.Ich habe benutzt
Vor; Ich denke, weil 'Exit' für mich aus irgendeinem Grund fehlgeschlagen ist. Die oben genannten Standardeinstellungen scheinen jedoch eine gute Idee zu sein.
quelle
Das hat mir jetzt schon eine Weile gut getan. Es druckt Fehler- oder Warnmeldungen in rot, eine Zeile pro Parameter, und ermöglicht einen optionalen Exit-Code.
quelle
Ich bin mir nicht sicher, ob dies für Sie hilfreich sein wird, aber ich habe einige der hier vorgeschlagenen Funktionen geändert, um die Überprüfung auf den Fehler (Code vom vorherigen Befehl beenden) darin aufzunehmen. Bei jeder "Prüfung" übergebe ich als Parameter auch die "Meldung", was der Fehler für Protokollierungszwecke ist.
Um es jetzt im selben Skript (oder in einem anderen, wenn ich es verwende
export -f error_exit
) aufzurufen, schreibe ich einfach den Namen der Funktion und übergebe eine Nachricht als Parameter wie folgt:Auf diese Weise konnte ich eine wirklich robuste Bash-Datei für einen automatisierten Prozess erstellen, die im Fehlerfall stoppt und mich benachrichtigt (
log.sh
wird das tun).quelle
function
Schlüsselworterror_exit() {
.cd /home/myuser/afolder || error_exit "Unable to switch to folder"
?Dieser Trick ist nützlich, wenn Befehle oder Funktionen fehlen. Der Name der fehlenden Funktion (oder ausführbaren Datei) wird in $ _ übergeben
quelle
$_
in der Funktion nicht das gleiche wie$?
? Ich bin mir nicht sicher, ob es einen Grund gibt, einen in der Funktion zu verwenden, aber nicht den anderen.Diese Funktion hat mir in letzter Zeit ziemlich gute Dienste geleistet:
Sie rufen es auf, indem Sie 0 oder den letzten Rückgabewert an den Namen des auszuführenden Befehls anhängen, damit Sie Befehle verketten können, ohne nach Fehlerwerten suchen zu müssen. Damit blockiert diese Anweisung:
Wird dies:
Wenn einer der Befehle fehlschlägt, wird der Fehlercode einfach an das Ende des Blocks übergeben. Ich finde es nützlich, wenn Sie nicht möchten, dass nachfolgende Befehle ausgeführt werden, wenn ein früherer fehlschlägt, aber Sie möchten auch nicht, dass das Skript sofort beendet wird (z. B. innerhalb einer Schleife).
quelle
Die Verwendung von Trap ist nicht immer eine Option. Wenn Sie beispielsweise eine wiederverwendbare Funktion schreiben, die eine Fehlerbehandlung erfordert und von jedem Skript aus aufgerufen werden kann (nachdem Sie die Datei mit Hilfsfunktionen bezogen haben), kann diese Funktion nichts über die Beendigungszeit des äußeren Skripts annehmen. Das macht die Verwendung von Fallen sehr schwierig. Ein weiterer Nachteil der Verwendung von Traps ist die schlechte Kompositionsfähigkeit, da Sie das Risiko eingehen, frühere Traps zu überschreiben, die möglicherweise früher in der Aufruferkette eingerichtet wurden.
Es gibt einen kleinen Trick, mit dem Fehler ohne Fallen richtig behandelt werden können. Wie Sie vielleicht bereits aus anderen Antworten wissen,
set -e
funktioniert dies nicht in Befehlen, wenn Sie den||
Operator danach verwenden, selbst wenn Sie sie in einer Subshell ausführen. zB würde das nicht funktionieren:Der
||
Bediener muss jedoch verhindern, dass er vor der Bereinigung von der äußeren Funktion zurückkehrt. Der Trick besteht darin, den inneren Befehl im Hintergrund auszuführen und dann sofort darauf zu warten. Daswait
eingebaute System gibt den Exit-Code des inneren Befehls zurück, und jetzt verwenden Sie||
afterwait
und nicht die innere Funktion, sodass sieset -e
in letzterer ordnungsgemäß funktioniert:Hier ist die generische Funktion, die auf dieser Idee aufbaut. Es sollte in allen POSIX-kompatiblen Shells funktionieren, wenn Sie es entfernen
local
Schlüsselwörter , dh allelocal x=y
durch nur ersetzenx=y
:Anwendungsbeispiel:
Beispiel ausführen:
Das einzige, was Sie bei der Verwendung dieser Methode beachten müssen, ist, dass alle Änderungen der Shell-Variablen, die von dem Befehl, an den Sie übergeben, vorgenommen wurden
run
, nicht an die aufrufende Funktion weitergegeben werden, da der Befehl in einer Subshell ausgeführt wird.quelle