Różnica między powrotem i wyjściem w funkcjach Bash

429

Jaka jest różnica między instrukcją returni exitw funkcjach Bash w odniesieniu do kodów wyjścia?

lecodesportif
źródło
55
Protip: wpisz help <command>swoją powłokę, aby uzyskać informacje o tym, co zrobi wbudowana powłoka. W twoim przypadku help returnihelp exit
SiegeX

Odpowiedzi:

318

Od man bashdnia return [n];

Powoduje, że funkcja przestaje działać i zwraca wartość określoną przez n do swojego obiektu wywołującego. Jeśli pominięto n, zwracany jest status ostatniego polecenia wykonanego w treści funkcji.

... w dniu exit [n]:

Powoduje zamknięcie powłoki ze statusem n. Jeśli n zostanie pominięte, kodem zakończenia jest status ostatniego wykonanego polecenia. Pułapka na EXIT jest wykonywana przed zakończeniem powłoki.

EDYTOWAĆ:

Zgodnie z edycją pytania dotyczącego kodów wyjścia, returnnie ma to nic wspólnego z kodami wyjścia. Kody wyjścia są przeznaczone dla aplikacji / skryptów , a nie funkcji. W związku z tym jedynym słowem kluczowym, które ustawia kod wyjścia skryptu (ten, który może zostać przechwycony przez program wywołujący za pomocą $?zmiennej powłoki) jestexit .

EDYCJA 2:

Moje ostatnie stwierdzenie, które odsyłam, exitpowoduje komentarze. Zostało stworzone w celu odróżnienia returni exitzrozumienia PO, a tak naprawdę w dowolnym punkcie skryptu programu / powłoki,exit jest to jedyny sposób na zakończenie skryptu kodem wyjścia dla procesu wywołującego.

Każda komenda wykonana w powłoce wytwarza lokalną „Kod wyjścia”: ustawia $?zmienną do tego kodu, i może być stosowany z if, &&i inni operatorzy warunkowo wykonywanie innych poleceń.

Te kody wyjścia (i wartość $? zmiennej) są resetowane przy każdym wykonaniu polecenia.

Nawiasem mówiąc, kod wyjścia ostatniego polecenia wykonanego przez skrypt jest używany jako kod wyjścia samego skryptu, jak widać w procesie wywołującym.

Wreszcie, wywołane funkcje działają jak polecenia powłoki względem kodów wyjścia. Kod wyjścia funkcji ( w ramach funkcji) ustawia się za pomocą return. Kiedy więc funkcja return 0jest uruchomiona, wykonywanie funkcji kończy się, dając kod wyjścia 0.

Diego Sevilla
źródło
10
Nie dokładnie. Zawsze zwraca wartość z bieżącej powłoki. Nie ma znaczenia, czy jesteś w funkcji, czy nie.
Diego Sevilla,
10
Skomentuj swoją edycję: Być może mylę wartości zwracane i kody wyjścia, ale func(){ return 50; };func;echo $?echo 50. Zatem $?zmienna powłoki nie wydaje się ograniczona exit.
lecodesportif,
8
$?Rozwija się do statusu wyjścia ostatnio wykonanego rurociągu pierwszego planu.” Wyjście może pochodzić z powłoki w formie wywołania exit(lub uderzenia w koniec skryptu) lub w formie wywołania returnfunkcji.
SiegeX,
11
@lecodesportif: $? Bieżący proces / skrypt jest ograniczony albo do exitwyniku ostatniego polecenia wykonanego przez ten skrypt. Tak więc, jeśli ostatni wiersz skryptu to wywołanie tej funkcji, a ta funkcja zwraca 50, tak, to $?, co produkujesz do procesu, który cię wezwał, to 50. Jednak to nie ma nic wspólnego z return, ponieważ jest to ograniczony do bieżącego skryptu. Zdarza się, że jest zwracane tylko wtedy, gdy to wywołanie funkcji jest ostatnim zdaniem skryptu. exit, jednak zawsze kończ skrypt i zwracaj tę wartość co $? do procesu wywoływania .
Diego Sevilla,
11
-1 za pomylenie mnie z linią „ returnnie ma nic wspólnego z kodami wyjścia”. Eksperymentowanie mówi mi, że nie ma funkcjonalnej różnicy między kodem powrotu funkcji a kodem wyjścia skryptu.
Jack
296

returnspowoduje, że bieżąca funkcja wyjdzie poza zakres, a exitskrypt zakończy się w punkcie, w którym zostanie wywołana. Oto przykładowy program, który pomoże to wyjaśnić:

#!/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"

Wynik

$ ./test.sh
this is retfunc()
We are still here
this is exitfunc()
SiegeX
źródło
17
Niezły przykład. Możesz także wyświetlić wartość wyjściową 1 cala $?.
Diego Sevilla,
48
Zauważ, że ta funkcja NIE wypisze „Jesteśmy nadal tutaj”, jeśli dodasz „set -e” przed wywołaniem „retfunc”.
Michael
4
Jednak echo fnord | while read x; do exitfunc; done; echo "still here"wydrukuje „wciąż tutaj”. Wydaje się, że whilew tym scenariuszu wychodzi tylko podpowłoka.
tripleee
Przybliżone obejście jest done || exit $?jednak brzydkie i nie do końca równoważne.
tripleee
2
+1 Przydatne może być dodanie: `` returnspowoduje, że bieżąca funkcja lub skrypt źródłowy wykroczą poza zakres ''.
Izaak
56

Nie sądzę, aby ktokolwiek tak naprawdę w pełni odpowiedział na pytanie, ponieważ nie opisuje, w jaki sposób są one używane. OK. Myślę, że wiemy, że exit zabija skrypt, gdziekolwiek jest on wywoływany, i można przypisać mu status, np. Exit lub exit 0 lub exit 7 i tak dalej. Można to wykorzystać do ustalenia, w jaki sposób skrypt został zmuszony do zatrzymania, jeśli został wywołany przez inny skrypt itp. Wystarczy przy wyjściu.

return, gdy zostanie wywołany, zwróci wartość określoną w celu wskazania zachowania funkcji, zwykle 1 lub 0. Na przykład:

    #!/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"
    }

sprawdź tak:

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

lub tak:

    isdirectory || echo "not a directory"

W tym przykładzie test można wykorzystać do wskazania, czy katalog został znaleziony. zauważ, że cokolwiek po zwrocie nie zostanie wykonane w funkcji. 0 to prawda, ale fałsz to 1 w powłoce, różny od innych języków prog.

Aby uzyskać więcej informacji na temat funkcji: http://www.linuxjournal.com/content/return-values-bash-functions

UWAGA: Funkcja isdirectory służy wyłącznie do celów instruktażowych. Nie powinno tak być w przypadku wykonywania takiej opcji w prawdziwym skrypcie.

Mike Q
źródło
3
Lub po prostu użyj, test -d $1aby osiągnąć ten sam wynik. Nigdy nie rób if <check> return else return. <check>sam zrobi to samo we wszystkich językach, które znam przynajmniej.
erikbwork
4
Aby być jeszcze bardziej wyraźnym na temat tego, co mówi erik: isdirectory() { [ -d "$1" ]; }zachowa się dokładnie tak samo, jak tutaj: Domyślna wartość zwracana przez funkcję powłoki, czy to po osiągnięciu końca kodu, czy też returnbez argumentów, jest wartością parametru ostatnie polecenie.
Charles Duffy
11
Inni komentatorzy tutaj krytykują styl przykładu Mike'a Q, kiedy tak naprawdę mówi o zachowaniu returnwypowiedzi. To prawda, że ​​jego przykład jest uproszczony i nie może być wykorzystywany w produkcji. Ale to proste, więc dobrze spełnia swoje zadanie. Nie ma w tym nic złego.
Mike S
Dzięki Mike S, tak, zgadzam się, że najprostszy przykład najlepiej wyjaśnia wyjście vs powrót. Pozostałe komentarze są z pewnością prawidłowe i należy je rozważyć w przypadku bardziej zaawansowanych koderów bash ;-)
Mike Q
1
Niektórzy mogą powiedzieć, że nie jest to dokładnie związane z pytaniem, ale dotyczyło i odpowiedziało na mój problem, który doprowadził mnie do znalezienia tego pytania. :-)
Jesse Steele
33

Pamiętaj, że funkcje są wewnętrzne dla skryptu i zwykle zwracają skąd zostały wywołane za pomocą instrukcji return. Wywołanie zewnętrznego skryptu to zupełnie inna sprawa, a skrypty zwykle kończą się instrukcją exit.

Różnica „między instrukcją return i exit w funkcjach BASH w odniesieniu do kodów wyjścia” jest bardzo niewielka. Oba zwracają status, a nie same wartości . Status zero oznacza sukces, a każdy inny status (od 1 do 255) oznacza awarię. Instrukcja return powróci do skryptu, z którego została wywołana, a instrukcja exit zakończy cały skrypt od momentu napotkania.

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).

Jeśli funkcja kończy się po prostu bez instrukcji return, status ostatniego wykonanego polecenia jest zwracany jako kod statusu (i zostanie umieszczony w $? ).

Pamiętaj, zwróć i wyjdź, zwróć kod stanu od 0 do 255, dostępny w $? . Nie możesz wstawić niczego innego do kodu statusu (np. Zwróć „cat”); to nie zadziała. Ale skrypt może przekazać 255 różnych przyczyn niepowodzenia za pomocą kodów stanu.

Możesz ustawić zmienne zawarte w skrypcie wywołującym lub echo skutkuje funkcją i użyć substytucji poleceń w skrypcie wywołującym; ale celem powrotu i wyjścia jest przekazanie kodów statusu, a nie wartości lub wyników obliczeń, jak można się spodziewać w języku programowania takim jak C.

użytkownik2100135
źródło
25

Czasami uruchamiasz skrypt za pomocą .lub source.

. a.sh

Jeśli dodasz exitdo a.shniego, nie tylko zakończy on skrypt, ale zakończy sesję powłoki.

Jeśli obejmują returnw a.sh, po prostu przestaje przetwarzać skrypt.

Juraj
źródło
1
Ale po uruchomieniu a.sh pojawia się błąd return: can only 'return' from a function or sourced script, który sprawia, że ​​nie nadaje się do ogólnego skryptu.
Peter - przywróć Monikę
Na najwyższym poziomie w skrypcie żadna nie jest odpowiednia w allsytuacjach. Użycie .lub sourceuruchomienie skryptu w bieżącej powłoce zamiast odradzania podpowłoki. Skrypt musi wiedzieć, jak go używać. Biada użytkownikowi, który robi to odwrotnie. Osobiście polecam przeczytanie skryptów przed uruchomieniem ich po raz pierwszy.
Jesse Chisholm
3
Niesamowita sztuczka, z jaką się spotkałem, to użyć trapfunkcji, ERR EXITa następnie najpierw zapisać kod wyjścia nieudanego polecenia, errCode=$?a następnie wyjść ze skryptu ( źródłowego lub nie), return $errCode || exit $errCodegdzie ||oznacza „jeśli nie mogę wrócić, ponieważ nie otrzymałem źródła , po prostu wyjdź zamiast ".
dragon788
6

W prostych słowach (głównie dla początkujących w kodowaniu) możemy powiedzieć:

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

Również jeśli zauważyłeś, jest to bardzo proste, ale ...

`return` : is the keyword
`exit()` : is the function
Jyo the Whiff
źródło
1
W skrypcie bash exitnie jest mniej więcej funkcją return. Są to wbudowane polecenia. Nie są to nawet słowa zastrzeżone.
Peter - Przywróć Monikę
6
  • exitzakończyć bieżący proces ; z kodem wyjścia lub bez niego, uważaj to za system bardziej niż funkcję programu. Zauważ, że podczas sourcingu exitzakończy działanie powłoki, jednak po uruchomieniu exitskryptu.

  • returnz funkcji wróć do instrukcji po wywołaniu, z kodem powrotu lub bez niego. returnjest opcjonalny i niejawny na końcu funkcji. returnmoże być używany tylko wewnątrz funkcji.

Chcę dodać, że podczas pozyskiwania exitskrypt nie jest łatwy do wykonania z poziomu funkcji bez zabicia powłoki. Myślę, że lepszy jest przykład skryptu „testowego”

#!/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"

wykonując następujące czynności:

user$ ./test
Whatever is not available
user$

test i skorupa się zamknie.

user$ . ./test
Whatever is not available

tylko test zakończy się i pojawi się monit.

Rozwiązaniem jest zawarcie potencjalnie procedury w (i)

#!/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

teraz w obu przypadkach tylko testwyjdzie.

fcm
źródło
Dodanie (i )umieszcza ten blok w podpowłoce, skutecznie nie wykonując polecenia .(źródłowego), tak jakbyś normalnie uruchomił skrypt testowy, który znajduje się w podpowłoce. Jeśli skrypt nie jest uruchamiany z, .lub sourcewtedy efektywnie masz 2 podpowłoki.
Jesse Chisholm
3

Pytanie OP: Jaka jest różnica między instrukcją return i exit w funkcjach BASH w odniesieniu do kodów wyjścia?

Po trzecie, potrzebne jest pewne wyjaśnienie:

  • Instrukcja (return | exit) nie jest wymagana do zakończenia wykonywania (powłoki | funkcji). (Funkcja | powłoka) zakończy się, gdy osiągnie koniec listy kodów, nawet bez instrukcji (return | exit).
  • Instrukcja (return | exit) nie jest wymagana do zwrócenia wartości z zakończonego (funkcja | shell). Każdy proces ma wbudowaną zmienną $? która zawsze ma wartość liczbową. Jest to specjalna zmienna, której nie można ustawić jak „? = 1”, ale można ją ustawić tylko w specjalny sposób (patrz poniżej *). Wartość $? po ostatnim poleceniu, które ma zostać wykonane w (nazywanej funkcji | powłoce dodatkowej), jest wartość przekazywana z powrotem do (funkcji wywołującej | powłoki nadrzędnej). Jest tak niezależnie od tego, czy ostatnim wykonanym poleceniem jest („return [n]” | ”exit [n]”), czy zwykły („return”, czy coś innego, co może być ostatnim poleceniem w wywoływanym kodzie funkcji.

Na powyższej liście punktorów wybierz z „(x | y)” albo zawsze pierwszy element, albo zawsze drugi element, aby uzyskać instrukcje dotyczące odpowiednio funkcji i powrotu lub powłok i wyjścia.

Oczywiste jest, że oboje mają wspólne zastosowanie specjalnej zmiennej $? przekazywać wartości w górę po ich zakończeniu.

* Teraz specjalne sposoby, że $? może być ustawiony:

  • Kiedy wywoływana funkcja kończy się i wraca do swojego wywołującego, to $? w dzwoniącym będzie równa końcowej wartości $? w funkcji zakończonej.
  • Gdy powłoka nadrzędna domyślnie lub jawnie czeka na pojedynczą podpowłokę i zostaje zwolniona przez zakończenie tej podpowłoki, wówczas $? w powłoce nadrzędnej będzie równa końcowej wartości $? w zakończonej podpowłoce.
  • Niektóre wbudowane funkcje mogą modyfikować $? w zależności od ich wyniku. Ale niektórzy nie.
  • Wbudowane funkcje „return” i „exit”, po których następuje argument liczbowy zarówno $? z argumentem i zakończ wykonywanie.

Warto zauważyć, że $? można przypisać wartość, wywołując exit w podpowłoce, tak jak to:

# (exit 259)
# echo $?
3  
Craig Hicks
źródło
4
W przypadku, gdy niektórzy go przegapili, wyświetl exit 259echo, 3ponieważ końcowa wartość wyjściowa to jeden bajt. 259 % 256 = 3
Jesse Chisholm
0

Przede wszystkim returnjest słowem kluczowym iexit mój przyjaciel jest funkcją.

To powiedziawszy, oto najprostsze wyjaśnienie.

return Zwraca wartość z funkcji.

exit Wychodzi z bieżącej powłoki lub ją opuszcza.

Ahmad Awais
źródło
Nie całkiem! Logicznie się mylisz. Wyjście jest funkcją, podczas gdy returnsłowem kluczowym. Zwrot to znacznie więcej niż tylko kody wyjścia, dlatego porównanie nie jest sprawiedliwe.
Ahmad Awais
Napisałem to, aby wyjaśnić, co chciałem wyjaśnić. Dzięki, że mi pomogłeś.
Ahmad Awais
4
Ani exitteż nie returnsą „słowami kluczowymi”, ani, jak to nazywa podręcznik bash, „słowami zastrzeżonymi”. Żaden z nich nie jest również „funkcją” w sensie funkcji bash. Oba są wbudowanymi poleceniami w języku bash. (Wywoływana jest standardowa funkcja biblioteki C exit(), a język programowania C ma słowo zastrzeżone return, ale nie należy ich mylić z poleceniami bash, mimo że ich semantyka jest dziwnie podobna.)
Peter - Przywróć Monicę