Ich schreibe ein Shell-Skript und muss überprüfen, ob eine Terminal-App installiert ist. Ich möchte einen TRY/CATCH-Befehl verwenden, um dies zu tun, es sei denn, es gibt einen eleganteren Weg.
Antworten
Zu viele Anzeigen?Gibt es einen TRY CATCH Befehl in Bash?
Nein.
Bash hat nicht so viele Annehmlichkeiten wie man sie in vielen Programmiersprachen finden kann.
Es gibt kein try/catch
in bash; man kann jedoch ein ähnliches Verhalten mit &&
oder ||
erreichen.
Verwendung von ||
:
Wenn Befehl1
fehlschlägt, wird Befehl2
wie folgt ausgeführt
Befehl1 || Befehl2
Ebenso wird mit &&
Befehl2
ausgeführt, wenn Befehl1
erfolgreich ist
Die nächste Annäherung an try/catch
ist wie folgt
{ # try
Befehl1 &&
# speichern Sie Ihre Ausgabe
} || { # catch
# speichern Sie das Protokoll für die Ausnahme
}
Auch bash enthält einige Fehlerbehandlungsmechanismen
set -e
es stoppt Ihr Skript, wenn ein einfacher Befehl fehlschlägt.
Und warum nicht if...else
. Es ist Ihr bester Freund.
Basierend auf einigen Antworten, die ich hier gefunden habe, habe ich mir eine kleine Hilfsdatei erstellt, die ich für meine Projekte verwenden kann:
trycatch.sh
#!/bin/bash
function try()
{
[[ $- = *e* ]]; SAVED_OPT_E=$?
set +e
}
function throw()
{
exit $1
}
function catch()
{
export ex_code=$?
(( $SAVED_OPT_E )) && set +e
return $ex_code
}
function throwErrors()
{
set -e
}
function ignoreErrors()
{
set +e
}
Hier ist ein Beispiel, wie es im Einsatz aussieht:
#!/bin/bash
export AnException=100
export AnotherException=101
# start with a try
try
( # open a subshell !!!
echo "tue etwas"
[ someErrorCondition ] && throw $AnException
echo "tue noch etwas"
executeCommandThatMightFail || throw $AnotherException
throwErrors # Beende automatisch den try-Block, wenn das Befehls-ergebnis nicht null ist
echo "jetzt zu etwas ganz anderem"
executeCommandThatMightFail
echo "es ist ein Wunder, dass wir so weit gekommen sind"
executeCommandThatFailsForSure || true # Ignoriere einen einzigen fehlschlagenden Befehl
ignoreErrors # Ignoriere Fehler von Befehlen bis auf weiteres
executeCommand1ThatFailsForSure
local result = $(executeCommand2ThatFailsForSure)
[ result != "erwarteter Fehler" ] && throw $AnException # ok, wenn es kein erwarteter Fehler ist, wollen wir abbrechen!
executeCommand3ThatFailsForSure
# Stellen Sie sicher, dass $ex_code gelöscht wird, ansonsten wird 'catch *' ausgeführt
# echo "fertig" erledigt den Trick für dieses Beispiel
echo "fertig"
)
# Direkt nachdem Sie die Subshell geschlossen haben, müssen Sie eine Verbindung zu 'catch' herstellen,
# indem Sie || und dann ein Gruppenzeichen anhängen
catch || {
# Jetzt können Sie behandeln
case $ex_code in
$AnException)
echo "AnException wurde ausgelöst"
;;
$AnotherException)
echo "AnotherException wurde ausgelöst"
;;
*)
echo "Es wurde eine unerwartete Ausnahme ausgelöst"
throw $ex_code # Sie können die "Ausnahme" erneut werfen, was bewirkt, dass das Skript abbricht, wenn sie nicht abgefangen wird
;;
esac
}
Ich habe eine nahezu fehlerlose Try & Catch-Implementierung in Bash entwickelt, die es Ihnen ermöglicht, Code wie folgt zu schreiben:
try
echo 'Hallo'
false
echo 'Dies wird nicht angezeigt'
catch
echo "Fehler in $__EXCEPTION_SOURCE__ in Zeile: $__EXCEPTION_LINE__!"
Sie können sogar die Try-Catch-Blöcke ineinander verschachteln!
try {
echo 'Hallo'
try {
echo 'Verschachteltes Hallo'
false
echo 'Dies wird nicht ausgeführt'
} catch {
echo "Verschachtelt erfasst (@ $__EXCEPTION_LINE__)"
}
false
echo 'Dies wird auch nicht ausgeführt'
} catch {
echo "Fehler in $__EXCEPTION_SOURCE__ in Zeile: $__EXCEPTION_LINE__!"
}
Der Code ist Teil meines Bash-Boilerplate/Frameworks. Es erweitert die Idee von Try & Catch mit Dingen wie Fehlerbehandlung mit Stapelverfolgung und Ausnahmen (plus einigen anderen nützlichen Funktionen).
Hier ist der Code, der nur für Try & Catch verantwortlich ist:
set -o pipefail
shopt -s expand_aliases
declare -ig __oo__insideTryCatch=0
# Wenn Try-Catch verschachtelt ist, dann setze +e davor, damit der übergeordnete Handler uns nicht erfasst
alias try="[[ \$__oo__insideTryCatch -gt 0 ]] && set +e;
__oo__insideTryCatch+=1; ( set -e;
trap \"Exception.Capture \${LINENO}; \" ERR;"
alias catch=" ); Exception.Extract \$? || "
Exception.Capture() {
local script="${BASH_SOURCE[1]#./}"
if [[ ! -f /tmp/stored_exception_source ]]; then
echo "$script" > /tmp/stored_exception_source
fi
if [[ ! -f /tmp/stored_exception_line ]]; then
echo "$1" > /tmp/stored_exception_line
fi
return 0
}
Exception.Extract() {
if [[ $__oo__insideTryCatch -gt 1 ]]
then
set -e
fi
__oo__insideTryCatch+=-1
__EXCEPTION_CATCH__=( $(Exception.GetLastException) )
local retVal=$1
if [[ $retVal -gt 0 ]]
then
# RÜCKWÄRTSKOMPATIBLE WEISE:
# export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-1)]}"
# export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[(${#__EXCEPTION_CATCH__[@]}-2)]}"
export __EXCEPTION_SOURCE__="${__EXCEPTION_CATCH__[-1]}"
export __EXCEPTION_LINE__="${__EXCEPTION_CATCH__[-2]}"
export __EXCEPTION__="${__EXCEPTION_CATCH__[@]:0:(${#__EXCEPTION_CATCH__[@]} - 2)}"
return 1 # damit wir mit einem "catch" fortfahren können
fi
}
Exception.GetLastException() {
if [[ -f /tmp/stored_exception ]] && [[ -f /tmp/stored_exception_line ]] && [[ -f /tmp/stored_exception_source ]]
then
cat /tmp/stored_exception
cat /tmp/stored_exception_line
cat /tmp/stored_exception_source
else
echo -e " \n${BASH_LINENO[1]}\n${BASH_SOURCE[2]#./}"
fi
rm -f /tmp/stored_exception /tmp/stored_exception_line /tmp/stored_exception_source
return 0
}
Fühlen Sie sich frei, es zu verwenden, zu duplizieren und beizutragen - es ist auf GitHub.
bash
beendet die laufende Ausführung nicht, wenn ein Fehlerzustand erkannt wird (es sei denn, Sie setzen das Flag -e
). Programmiersprachen, die try/catch
anbieten, tun dies, um ein "Abbruch" aufgrund dieser besonderen Situation zu verhindern (typischerweise "Ausnahme" genannt).
In bash
hingegen wird nur der betreffende Befehl mit einem Exit-Code größer als 0 beendet, um den Fehlerzustand anzuzeigen. Natürlich können Sie das überprüfen, aber da es kein automatisches Abbrechen von allem gibt, ergibt ein try/catch keinen Sinn. Es fehlt einfach dieser Kontext.
Sie können jedoch ein Abbrechen simulieren, indem Sie Sub-Shell verwenden, die an einem von Ihnen festgelegten Punkt beendet werden kann:
(
echo "Etwas tun"
echo "Etwas anderes tun"
if some_condition
then
exit 3 # <-- dies ist unser simuliertes Abbruch
fi
echo "Noch etwas anderes tun"
echo "Und zuletzt etwas tun"
) # <-- hier kommen wir nach dem simulierten Abbruch an, und $? wird 3 sein (Exit-Code)
if [ $? = 3 ]
then
echo "Abbruch erkannt"
fi
Anstelle von some_condition
mit einem if
können Sie auch einfach einen Befehl versuchen und im Falle eines Fehlers (Exit-Code größer als 0) abbrechen:
(
echo "Etwas tun"
echo "Etwas anderes tun"
some_command || exit 3
echo "Noch etwas anderes tun"
echo "Und zuletzt etwas tun"
)
...
Leider sind Sie bei Verwendung dieser Technik auf 255 verschiedene Exit-Codes (1..255) beschränkt, und es können keine vernünftigen Ausnahmeobjekte verwendet werden.
Wenn Sie weitere Informationen benötigen, die mit Ihrer simulierten Ausnahme weitergegeben werden sollen, können Sie den Standardausgang der Subshells verwenden, aber das ist etwas kompliziert und vielleicht eine andere Frage ;-)
Mit dem oben genannten Flag -e
für die Shell können Sie sogar diese explizite exit
-Anweisung entfallen lassen:
(
set -e
echo "Etwas tun"
echo "Etwas anderes tun"
some_command
echo "Noch etwas anderes tun"
echo "Und zuletzt etwas tun"
)
...
- See previous answers
- Weitere Antworten anzeigen