Unterschied zwischen Return und Exit in Bash-Funktionen

429

Was ist der Unterschied zwischen der returnund exit-Anweisung in Bash-Funktionen in Bezug auf Exit-Codes?

lecodesportif
quelle
55
Protip: Geben Sie help <command>Ihre Shell ein, um Informationen darüber zu erhalten, was eine eingebaute Shell tun wird. In Ihrem Fall help returnundhelp exit
SiegeX

Antworten:

318

Von man bashan return [n];

Bewirkt, dass eine Funktion die Ausführung beendet und den von n angegebenen Wert an ihren Aufrufer zurückgibt. Wenn n weggelassen wird, ist der Rückgabestatus der des letzten im Funktionskörper ausgeführten Befehls.

... am exit [n]:

Bewirkt, dass die Shell mit dem Status n beendet wird. Wenn n weggelassen wird, ist der Exit-Status der des zuletzt ausgeführten Befehls. Ein Trap bei EXIT wird ausgeführt, bevor die Shell beendet wird.

BEARBEITEN:

Laut Ihrer Bearbeitung der Frage in Bezug auf Exit-Codes returnhat dies nichts mit Exit-Codes zu tun. Exit-Codes sind für Anwendungen / Skripte vorgesehen , nicht für Funktionen. In dieser Hinsicht ist das einzige Schlüsselwort, das den Exit-Code des Skripts festlegt (das vom aufrufenden Programm mithilfe der $?Shell-Variablen abgefangen werden kann) exit.

EDIT 2:

Meine letzte Aussage, die sich bezieht, exitverursacht einige Kommentare. Es wurde erstellt, um das OP zu differenzieren returnund exitzu verstehen, und tatsächlich ist es an jedem Punkt eines Programm- / Shell-Skripts exitdie einzige Möglichkeit, das Skript mit einem Exit-Code für den aufrufenden Prozess zu beenden.

Jedes in dem Shell ausgeführten Befehl erzeugt einen lokalen „Exit - Code“: es ist die Sätze $?variabel auf diesen Code und kann mit verwendet werden if, &&und andere Betreiber bedingt andere Befehle auszuführen.

Diese Exit-Codes (und der Wert der $?Variablen) werden bei jeder Befehlsausführung zurückgesetzt.

Im Übrigen wird der Exit-Code des letzten vom Skript ausgeführten Befehls als Exit-Code des Skripts selbst verwendet, wie vom aufrufenden Prozess gesehen.

Schließlich fungieren Funktionen beim Aufruf als Shell-Befehle in Bezug auf Exit-Codes. Der Exit-Code der Funktion ( innerhalb der Funktion) wird mithilfe von festgelegt return. Wenn also eine Funktion ausgeführt return 0wird, wird die Funktionsausführung beendet und der Exit-Code 0 angegeben.

Diego Sevilla
quelle
10
Nicht genau. Es wird immer ein Wert aus der aktuellen Shell zurückgegeben. Es spielt keine Rolle, ob Sie sich in einer Funktion befinden oder nicht.
Diego Sevilla
10
Kommentar zu Ihrer Bearbeitung: Ich kann Rückgabewerte und Exit-Codes verwirren, aber es gibt func(){ return 50; };func;echo $?50 wieder. Die $?Shell-Variable scheint also nicht auf beschränkt zu sein exit.
Lecodesportif
8
" $?Wird auf den Exit-Status der zuletzt ausgeführten Vordergrund-Pipeline erweitert." Dieser Exit kann aus der Shell in Form eines Aufrufs exit(oder am Ende des Skripts) oder in Form eines Aufrufs returninnerhalb einer Funktion erfolgen.
SiegeX
11
@lecodesportif: Der $? aktuelle Prozess / das aktuelle Skript ist entweder auf exitoder auf das Ergebnis des letzten von diesem Skript ausgeführten Befehls beschränkt. Wenn Ihre letzte Skriptzeile der Aufruf dieser Funktion ist und diese Funktion 50 zurückgibt, ist die $?, die Sie für den Prozess erzeugen , der Sie aufgerufen hat, 50. Dies hat jedoch nichts mit dem zu tun return, da dies der Fall ist beschränkt auf das aktuelle Skript. Es wird nur zurückgegeben, wenn dieser Funktionsaufruf der letzte Satz des Skripts ist. exitBeenden Sie jedoch immer das Skript und geben Sie diesen Wert $? für den aufrufenden Prozess zurück .
Diego Sevilla
11
-1 für die Verwechslung mit der Zeile " returnhat nichts mit Exit-Codes zu tun." Experimente zeigen mir, dass es keinen funktionalen Unterschied zwischen dem Rückkehrcode einer Funktion und dem Exit-Code eines Skripts gibt.
Jack
296

returnbewirkt, dass die aktuelle Funktion den Gültigkeitsbereich verlässt, während exitdas Skript an der Stelle endet, an der es aufgerufen wird. Hier ist ein Beispielprogramm, um dies zu erklären:

#!/bin/bash

retfunc()
{
    echo "this is retfunc()"
    return 1
}

exitfunc()
{
    echo "this is exitfunc()"
    exit 1
}

retfunc
echo "We are still here"
exitfunc
echo "We will never see this"

Ausgabe

$ ./test.sh
this is retfunc()
We are still here
this is exitfunc()
SiegeX
quelle
17
Schönes Beispiel. Sie können auch den Exit-Wert 1 in anzeigen $?.
Diego Sevilla
48
Beachten Sie, dass diese Funktion NICHT "Wir sind noch hier" druckt, wenn Sie vor dem Aufruf von "retfunc" "set -e" hinzufügen.
Michael
4
Allerdings echo fnord | while read x; do exitfunc; done; echo "still here"druckt „immer noch hier“. Es scheint, dass whilein diesem Szenario nur die Sub-Shell beendet wird.
Tripleee
Eine ungefähre Problemumgehung ist done || exit $?aber das ist hässlich und nicht genau gleichwertig.
Tripleee
2
+1 Es kann nützlich sein, Folgendes hinzuzufügen: `` ` returnbewirkt, dass die aktuelle Funktion oder das Sourcing-Skript den Gültigkeitsbereich verlässt```.
Isaac
56

Ich glaube nicht, dass jemand die Frage wirklich vollständig beantwortet hat, weil er nicht beschreibt, wie die beiden verwendet werden. OK Ich denke, wir wissen, dass exit das Skript beendet, wo immer es aufgerufen wird, und Sie können ihm auch einen Status zuweisen, z. B. exit oder exit 0 oder exit 7 und so weiter. Dies kann verwendet werden, um zu bestimmen, wie das Skript angehalten werden musste, wenn es von einem anderen Skript aufgerufen wurde usw. Genug beim Beenden.

return beim Aufruf gibt den angegebenen Wert zurück, der das Verhalten der Funktion angibt, normalerweise eine 1 oder eine 0. Beispiel:

    #!/bin/bash
    isdirectory() {
      if [ -d "$1" ]
      then
        return 0
      else
        return 1
      fi
    echo "you will not see anything after the return like this text"
    }

Überprüfen Sie wie folgt:

    if isdirectory $1; then echo "is directory"; else echo "not a directory"; fi

oder so:

    isdirectory || echo "not a directory"

In diesem Beispiel kann der Test verwendet werden, um anzuzeigen, ob das Verzeichnis gefunden wurde. Beachten Sie, dass nach der Rückgabe nichts in der Funktion ausgeführt wird. 0 ist wahr, aber falsch ist 1 in der Shell, anders als bei anderen Prog langs.

Weitere Informationen zu Funktionen: http://www.linuxjournal.com/content/return-values-bash-functions

HINWEIS: Die isdirectory-Funktion dient nur zu Unterrichtszwecken. Dies sollte nicht die Art und Weise sein, wie Sie eine solche Option in einem echten Skript ausführen.

Mike Q.
quelle
3
Oder verwenden Sie einfach test -d $1, um das gleiche Ergebnis zu erzielen. Mach das niemals if <check> return else return. <check>allein wird das gleiche in allen Sprachen tun, die ich zumindest kenne.
Erikbwork
4
Um noch deutlicher zu sagen, was erik sagt: isdirectory() { [ -d "$1" ]; }verhält sich genauso wie hier: Der Standardrückgabewert einer Shell-Funktion, sei es durch Erreichen des Codeendes oder durch a returnohne Argumente, ist der von letzter Befehl.
Charles Duffy
11
Die anderen Kommentatoren hier kritisieren den Stil von Mike Qs Beispiel, wenn er wirklich über das Verhalten der returnAussage spricht . Es ist wahr, dass sein Beispiel simpel ist und nicht in der Produktion verwendet werden darf. Aber es ist einfach, also erfüllt es seine Aufgabe ganz gut. Daran ist nichts auszusetzen.
Mike S
Danke Mike S, ja, ich stimme zu, dass das einfachste Beispiel Exit vs Return am besten erklärt. Die anderen Kommentare sind sicherlich gültig und sollten für fortgeschrittenere Bash-Codierer in Betracht gezogen werden ;-)
Mike Q
1
Einige mögen sagen, dass es nicht genau mit der Frage zusammenhängt, aber es bezog sich auf das Problem, das ich hatte und das mich dazu brachte, diese Frage zu finden, und beantwortete es. :-)
Jesse Steele
33

Denken Sie daran, dass Funktionen skriptintern sind und normalerweise mithilfe der return-Anweisung zurückgegeben werden, von wo aus sie aufgerufen wurden. Das Aufrufen eines externen Skripts ist eine ganz andere Sache, und Skripte werden normalerweise mit einer exit-Anweisung beendet.

Der Unterschied "zwischen der return- und exit-Anweisung in BASH-Funktionen in Bezug auf Exit-Codes" ist sehr gering. Beide geben einen Status zurück, keine Werte an sich. Ein Status von Null zeigt Erfolg an, während jeder andere Status (1 bis 255) einen Fehler anzeigt. Die return-Anweisung kehrt zu dem Skript zurück, von dem aus sie aufgerufen wurde, während die exit-Anweisung das gesamte Skript von der Stelle aus beendet, an der sie angetroffen wird.

return 0  # returns to where the function was called.  $? contains 0 (success).

return 1  # returns to where the function was called.  $? contains 1 (failure).

exit 0  # exits the script completely.  $? contains 0 (success).

exit 1  # exits the script completely.  $? contains 1 (failure).

Wenn Ihre Funktion einfach ohne return-Anweisung endet, wird der Status des zuletzt ausgeführten Befehls als Statuscode zurückgegeben (und in eingefügt $?).

Denken Sie daran, dass beim Zurück- und Beenden ein Statuscode von 0 bis 255 zurückgegeben wird, der in verfügbar ist $?. Sie können nichts anderes in einen Statuscode einfügen (z. B. "cat" zurückgeben). es wird nicht funktionieren. Ein Skript kann jedoch mithilfe von Statuscodes 255 verschiedene Fehlergründe zurückgeben.

Sie können im aufrufenden Skript enthaltene Variablen festlegen oder die Ergebnisse in der Funktion wiedergeben und die Befehlssubstitution im aufrufenden Skript verwenden. Der Zweck von Return und Exit besteht jedoch darin, Statuscodes zu übergeben, nicht Werte oder Berechnungsergebnisse, wie man es in einer Programmiersprache wie C erwarten kann.

user2100135
quelle
25

Manchmal führen Sie ein Skript mit .oder aus source.

. a.sh

Wenn Sie ein exitin das a.sheinfügen, wird nicht nur das Skript beendet, sondern auch Ihre Shell-Sitzung beendet.

Wenn Sie ein returnin das a.sheinfügen, wird die Verarbeitung des Skripts einfach beendet.

Juraj
quelle
1
Aber wenn ich nur a.sh ausführe, erhalte ich eine Fehlermeldung return: can only 'return' from a function or sourced script, die es für ein allgemeines Skript ungeeignet macht.
Peter - Monica am
Auf der obersten Ebene eines Skripts ist keines in allSituationen geeignet . Verwenden .oder sourceAusführen des Skripts in der aktuellen Shell, anstatt eine Unter-Shell zu erzeugen. Das Skript muss wissen, wie es verwendet werden soll. Wehe dem Benutzer, der es gegenüber tut. Persönlich empfehle ich, Skripte zu lesen, bevor Sie sie zum ersten Mal ausführen.
Jesse Chisholm
3
Ein großartiger Trick, auf den ich gestoßen bin, besteht darin, eine trapFunktion für zu verwenden ERR EXITund dann zuerst den Exit-Code eines fehlgeschlagenen Befehls zu speichern errCode=$?und dann das Skript (bezogen oder nicht) mit return $errCode || exit $errCodeden ||Mitteln zu beenden : "Wenn ich nicht zurückkehren kann, weil ich nicht beschafft wurde." , stattdessen einfach beenden ".
Dragon788
6

In einfachen Worten (hauptsächlich für Anfänger in der Codierung) können wir sagen:

`return` : exits the function,
`exit()` : exits the program(called as process while running)

Auch wenn Sie beobachtet haben, ist dies sehr einfach, aber ...,

`return` : is the keyword
`exit()` : is the function
Jyo der Hauch
quelle
1
In einem Bash-Skript exitist nicht mehr oder weniger eine Funktion als return. Sie sind integrierte Befehle. Sie sind nicht einmal reservierte Wörter.
Peter - Monica wieder einsetzen
6
  • exitBeenden Sie den aktuellen Prozess . Betrachten Sie dies mit oder ohne Exit-Code eher als ein System als als eine Programmfunktion. Beachten Sie, dass beim Sourcing exitdie Shell beendet wird, beim Ausführen jedoch nur exitdas Skript.

  • returnVon einer Funktion kehren Sie nach dem Aufruf mit oder ohne Rückkehrcode zur Anweisung zurück. returnist optional und am Ende der Funktion implizit. returnkann nur innerhalb einer Funktion verwendet werden.

Ich möchte hinzufügen, dass es während der Beschaffung nicht einfach ist, exitdas Skript innerhalb einer Funktion zu erstellen, ohne die Shell zu beenden. Ich denke, ein Beispiel ist besser für ein 'Test'-Skript

#!/bin/bash
function die(){
   echo ${1:=Something terrible wrong happen}
   #... clean your trash
   exit 1
}

[ -f /whatever/ ] || die "whatever is not available"
# now we can proceed
echo "continue"

Gehen Sie wie folgt vor:

user$ ./test
Whatever is not available
user$

test -und- die Shell wird geschlossen.

user$ . ./test
Whatever is not available

Nur testwird beendet und die Eingabeaufforderung wird angezeigt.

Die Lösung besteht darin, das potenzielle Verfahren in (und einzuschließen)

#!/bin/bash
function die(){
   echo $(1:=Something terrible wrong happen)
   #... clean your trash
   exit 1
}

( # added        
    [ -f /whatever/ ] || die "whatever is not available"
    # now we can proceed
    echo "continue"
) # added

Jetzt wird in beiden Fällen nur beendet test.

fcm
quelle
Durch Hinzufügen von (und wird )dieser Block in eine Unter-Shell .eingefügt, wodurch der Befehl (Quelle) effektiv aufgehoben wird, als hätten Sie das Testskript normal ausgeführt, das sich in einer Unter-Shell befindet. Wenn das Skript nicht mit ausgeführt wird .oder sourceSie effektiv 2 Unterschalen haben.
Jesse Chisholm
3

Die Frage des OP: Was ist der Unterschied zwischen der return- und der exit-Anweisung in BASH-Funktionen in Bezug auf Exit-Codes?

Zunächst ist eine Klarstellung erforderlich:

  • Eine Anweisung (return | exit) ist nicht erforderlich, um die Ausführung einer (function | shell) zu beenden. Eine (Funktion | Shell) wird beendet, wenn sie das Ende ihrer Codeliste erreicht, auch ohne Anweisung (return | exit).
  • Eine Anweisung (return | exit) ist nicht erforderlich, um einen Wert von einer terminierten (function | shell) zurückzugeben. Jeder Prozess hat eine eingebaute Variable $? das hat immer einen numerischen Wert. Es ist eine spezielle Variable, die nicht wie "? = 1" gesetzt werden kann, sondern nur auf spezielle Weise (siehe unten *). Der Wert von $? Nach dem letzten Befehl, der in der (aufgerufenen Funktion | Unter-Shell) ausgeführt wird, wird der Wert an die (Funktionsaufrufer | Übergeordnete Shell) zurückgegeben. Dies gilt unabhängig davon, ob der zuletzt ausgeführte Befehl ("return [n]" | "exit [n]") oder plain ("return" oder etwas anderes ist, das zufällig der letzte Befehl im aufgerufenen Funktionscode ist.

Wählen Sie in der obigen Aufzählungsliste aus "(x | y)" entweder immer das erste Element oder immer das zweite Element, um Anweisungen zu Funktionen & Rückgabe oder Shells & Beenden zu erhalten.

Klar ist, dass beide die spezielle Variable $ gemeinsam verwenden? Werte nach Beendigung nach oben zu übergeben.

* Nun zu den besonderen Möglichkeiten, die $? kann eingestellt werden:

  • Wenn eine aufgerufene Funktion beendet wird und zu ihrem Aufrufer zurückkehrt, dann $? im Anrufer wird gleich dem Endwert von $ sein? in der beendeten Funktion.
  • Wenn eine übergeordnete Shell implizit oder explizit auf eine einzelne Sub-Shell wartet und durch Beendigung dieser Sub-Shell freigegeben wird, wird $? in der übergeordneten Shell wird gleich dem Endwert von $? in der terminierten Subshell.
  • Einige integrierte Funktionen können $? abhängig von ihrem Ergebnis. Aber manche nicht.
  • Integrierte Funktionen "return" und "exit", gefolgt von einem numerischen Argument, beide $? mit Argument und beenden Sie die Ausführung.

Es ist erwähnenswert, dass $? kann durch Aufrufen von exit in einer Sub-Shell wie folgt einen Wert zugewiesen werden:

# (exit 259)
# echo $?
3  
Craig Hicks
quelle
4
Falls einige es verpasst haben, exit 259hallt dies wider, 3weil der endgültige Exit-Wert ein einzelnes Byte ist. 259 % 256 = 3
Jesse Chisholm
0

Zuallererst returnist ein Schlüsselwort und exitmein Freund ist eine Funktion.

Das heißt, hier ist eine einfachste Erklärung.

return Es gibt einen Wert von einer Funktion zurück.

exit Es verlässt die aktuelle Shell oder verlässt sie.

Ahmad Awais
quelle
Nicht wirklich! Sie sind logisch falsch. Beenden ist eine Funktion, während returnes sich um ein Schlüsselwort handelt. Return ist viel mehr als nur Exit-Codes, weshalb der Vergleich nicht fair ist.
Ahmad Awais
Ich habe es bearbeitet, um den Punkt klarer zu machen, den ich machen wollte. Danke, dass du mir dabei geholfen hast.
Ahmad Awais
4
Weder exitnoch return"Schlüsselwörter" oder, wie das Bash-Handbuch sie nennt, "reservierte Wörter". Keiner von beiden ist eine "Funktion" im Sinne einer Bash-Funktion. Beide sind eingebaute Befehle in Bash-Jargon. (Es gibt eine C-Standardbibliotheksfunktion namens exit(), und die C-Programmiersprache hat ein reserviertes Wort return, aber diese sollten nicht mit den Bash-Befehlen verwechselt werden, obwohl ihre Semantik merkwürdig ähnlich ist.)
Peter - Reinstate Monica