Dowolny sposób na wyjście ze skryptu bash, ale bez zamykania terminala

183

Kiedy użyję exitpolecenia w skrypcie powłoki, skrypt zakończy terminal (monit). Czy istnieje sposób na zakończenie skryptu, a następnie pozostanie w terminalu?

run.shOczekuje się, że mój skrypt zostanie wykonany bezpośrednio z innego źródła lub z innego skryptu.

EDYCJA: Aby być bardziej szczegółowym, istnieją dwa skrypty run2.shjako

...
. run.sh
echo "place A"
...

i run.shjak

...
exit
...

kiedy . run2.shgo exitmijam , a jeśli wpada w kodod run.sh, chcę, aby zatrzymał się na terminalu i pozostał tam. Ale przy użyciu exitcały terminal zostaje zamknięty.

PS: Próbowałem użyć return, ale echokodeline nadal będzie wykonywany ....

Richard
źródło
5
Naprawdę, naprawdę, naprawdę muszę zapytać: dlaczego używasz wyjścia w skrypcie źródłowym?
Ignacio Vazquez-Abrams,
3
polecenie exit nie powinno kończyć sesji terminalu / logowania. jeśli użyjesz exit 0do zakończenia skryptu po sukcesie, po uruchomieniu skryptu np: ./test.shpowinieneś zobaczyć wynik, ale konsola pozostanie otwarta.
Ben Ashton,
Możesz użyć shellpolecenia, które faktycznie otwiera terminal powłoki. Jednak z własnego doświadczenia wynika, że ​​tak się nie dzieje exit. Wyjście zwykle zwraca kontrolę nad skryptem nadrzędnym.
Willem Van Onsem,

Odpowiedzi:

258

„Problem” naprawdę polega na tym, że pozyskujesz i nie wykonujesz skryptu. Gdy źródło pliku, jego zawartość zostanie wykonana w bieżącej powłoce, zamiast odradzania podpowłoki. Więc wszystko, łącznie z wyjściem, wpłynie na bieżącą powłokę.

Zamiast używać exit, będziesz chciał użyć return.

Dominik Honnef
źródło
2
Oto inne wyjaśnienie, które uznałem za pomocne: askubuntu.com/a/53179/148337
3cheesewheel
Chociaż poprawna, nie jest to świetna odpowiedź. Ignoruje to, że skrypt wywołujący może deklarować zmienne lub funkcje, do których skrypt wywoływany potrzebuje dostępu. Lepiej wyjaśnić, jak ustawić kod powrotu, a następnie przetworzyć go w runs.sh@ruakh, ma lepszą odpowiedź na to pytanie.
MikeSchinkel,
5
Co jeśli funkcja jest wywołaniem zagnieżdżonym? tj. a połączenia b, b połączenia c, c chce natychmiast wyjść z a i b.
Michael
Źródło jest prawie takie samo jak kopiowanie wklej. Lepiej jest użyć sh <script>lub bash <script>jeśli ktoś chce uruchomić skrypt i zakończyć w pewnym momencie
peterchaula
42

Tak; możesz użyć returnzamiast exit. Jego głównym celem jest powrót z funkcji powłoki, ale jeśli użyjesz jej w sourceskrypcie -d, ona zwróci z tego skryptu.

Zgodnie z §4.1 „Wbudowane powłoki Bourne'a” podręcznika Bash Reference Manual :

     return [n]

Powoduje, że funkcja powłoki kończy działanie, zwracając wartość n . Jeśli nie podano n , zwracana wartość to status wyjścia ostatniego polecenia wykonanego w funkcji. Może to być również użyte do zakończenia wykonywania skryptu wykonywanego z wbudowanym .(lub source) programem, zwracając albo n, albo status zakończenia ostatniego polecenia wykonanego w skrypcie jako status wyjścia skryptu. Wszelkie polecenia związane z RETURNpułapką są wykonywane przed wznowieniem wykonania po funkcji lub skrypcie. Zwracany status jest różny od zera, jeśli returnjest używany poza funkcją, a nie podczas wykonywania skryptu przez .lub source.

ruakh
źródło
3
returnmożna używać TYLKO z funkcji. Jeśli użyjesz go returni wykonasz jako skrypt powłoki (np. sh run.sh), return: can only Bash
zgłosi
@TzungsingDavidWong: Zdajesz sobie sprawę, że większość tej odpowiedzi jest cytatem z oficjalnej instrukcji? I że podany przez ciebie komunikat o błędzie zgadza się z tą odpowiedzią zamiast z twoim twierdzeniem?
ruakh
Nie zgadzam się z tobą. Chcę tylko zaznaczyć, że returnto nie zadziała, jeśli skrypt zostanie uruchomiony jako skrypt powłoki i nie zostanie wykonany przez. (lub source). BTW, gdzie mogę znaleźć dokument source -d?
Tzunghsing David Wong
9

Zamiast uruchamiać skrypt za pomocą . run2.sh, możesz go uruchomić za pomocą sh run2.shlubbash run2.sh

Zostanie uruchomiona nowa podpowłoka, aby uruchomić skrypt, zostanie ona zamknięta na końcu skryptu, pozostawiając otwartą drugą powłokę.

Viorel Mirea
źródło
Jeśli tak, to dlaczego drugi parametr sh "." run2.sh?
CHAN
@ H0WARD Masz rację, zapomniałem usunąć kropki. Zredagowałem odpowiedź teraz.
Viorel Mirea,
1
PO wyraźnie określa źródło jako wymóg.
Jonathan Neufeld,
4

Możesz dodać dodatkową komendę exit po instrukcji / komendzie return, aby działała w obu przypadkach, wykonując skrypt z wiersza komend i pozyskując go z terminala.

Przykładowy kod wyjścia w skrypcie:

   if [ $# -lt 2 ]; then
     echo "Needs at least two arguments"
     return 1 2>/dev/null
     exit 1
   fi

Linia z exitpoleceniem nie będzie wywoływana, gdy skrypt zostanie pobrany po returnpoleceniu.

Podczas wykonywania skryptu returnpolecenie podaje błąd. Dlatego pomijamy komunikat o błędzie, przekazując go do /dev/null.

bez nazwy
źródło
3

Właściwie myślę, że możesz być zdezorientowany tym, jak uciekasz run a script.

Jeśli użyjesz shdo uruchomienia skryptu, powiedzmy, sh ./run2.shnawet jeśli osadzony skrypt kończy się na exit, okno terminala nadal pozostanie.

Jeśli jednak użyjesz .lub source, okno terminala również się zamknie / zamknie, gdy indeks dolny się skończy.

Aby uzyskać więcej informacji, zobacz Jaka jest różnica między używaniem sha source?

Karl Li
źródło
2

To tak, jakbyś umieścił funkcję uruchamiania w skrypcie run2.sh. Używasz kodu wyjścia w trakcie uruchamiania, podczas gdy źródłowy plik run2.sh znajduje się w bash tty. Jeśli funkcja run daje moc wyjścia ze skryptu i daje run2.sh moc wyjścia z terminatora. Zatem, ponieważ funkcja Run ma moc, aby wyjść z teminatora.

    #! /bin/sh
    # use . run2.sh

    run()
    {
        echo "this is run"
        #return 0
        exit 0
    }

    echo "this is begin"
    run
    echo "this is end"

W każdym razie zgadzam się z Kazem, że to problem projektowy.

Umae
źródło
Z tekstu nie wynika jasno, co próbuje zrobić ta odpowiedź, ale z pewnością nie rozwiązuje ona obecnego pytania.
Luís de Sousa
2

Miałem ten sam problem i z powyższych odpowiedzi oraz z tego, co zrozumiałem, ostatecznie działało dla mnie:

  1. Mieć linię shebang, która wywołuje zamierzony skrypt, na przykład

    #!/bin/bashsłuży bashdo wykonania skryptu

Mam skrypty z oboma rodzajami shebang. Z tego powodu używanie shlub .nie było niezawodne, ponieważ prowadziło to do błędnego wykonania (na przykład, gdy skrypt nie działa poprawnie)

Odpowiedź brzmiała więc:

    • Upewnij się, że skrypt ma shebang, aby nie było wątpliwości co do zamierzonego programu obsługi.
    • chmod plik .sh, aby można go było wykonać. (chmod +x file.sh)
    • Wywołaj go bezpośrednio bez żadnego shlub.

      (./myscript.sh)

Mam nadzieję, że to pomoże komuś z podobnym pytaniem lub problemem.

gk_2000
źródło
1

Myślę, że tak się dzieje, ponieważ używasz go w trybie źródłowym z kropką

. myscript.sh

Powinieneś uruchomić to w podpowłoce:

/full/path/to/script/myscript.sh

„source” http://ss64.com/bash/source.html

JBoy
źródło
Jeśli to . myscript.shdziała, nie potrzebujesz pełnej ścieżki do skryptu . Co najwyżej możesz potrzebować ./myscript.sh.
Álvaro González
1

Jak słusznie zauważyli inni , słuszne jest, że skrypty źródłowe i wykonywane używają returnvs., exitaby utrzymać tę samą sesję otwartą.

Oto pokrewna wskazówka, jeśli kiedykolwiek potrzebujesz skryptu, który powinien utrzymywać sesję otwartą, niezależnie od tego, czy jest ona pozyskiwana, czy nie.

Poniższy przykład można uruchomić bezpośrednio jak foo.shlub pozyskać jak . foo.sh/ source foo.sh. W obu przypadkach sesja pozostanie otwarta po „wyjściu”. $@Łańcuch jest przekazywany tak, że funkcja ma dostęp do argumentów zewnętrznej skryptu.

#!/bin/sh
foo(){
    read -p "Would you like to XYZ? (Y/N): " response;
    [ $response != 'y' ] && return 1;
    echo "XYZ complete (args $@).";
    return 0;
    echo "This line will never execute.";
}
foo "$@";

Wynik końcowy:

$ foo.sh
$ Czy chciałbyś XYZ? (T / N): n
$. foo.sh
$ Czy chciałbyś XYZ? (T / N): n
$ |
(okno terminala pozostaje otwarte i akceptuje dodatkowe dane wejściowe)

Może to być przydatne do szybkiego testowania zmian skryptu w jednym terminalu przy jednoczesnym trzymaniu paczki złomu pod głównym exit/ returnpodczas pracy. Może także sprawić, że kod stanie się bardziej przenośny w pewnym sensie (jeśli masz mnóstwo skryptów, które można wywoływać na różne sposoby lub nie), chociaż jest o wiele mniej niezręczny w użyciu returni exittam, gdzie jest to właściwe.

Beejor
źródło
0

jeśli twój emulator terminala nie ma tego -hold, możesz zdezynfekować skrypt pochodzący od źródła i zatrzymać terminal za pomocą:

#!/bin/sh
sed "s/exit/return/g" script >/tmp/script
. /tmp/script
read

w przeciwnym razie możesz użyć $TERM -hold -e script

technozaur
źródło
0

Zwróć również uwagę na zwrot z oczekiwaną wartością zwrotu. W przeciwnym razie, jeśli użyjesz wyjścia, gdy napotkasz wyjście, wyjdzie ono z podstawowej powłoki, ponieważ źródło nie tworzy innego procesu (instancji).

coder23
źródło
0

Aby napisać skrypt, który jest bezpieczny być uruchamiany zarówno jako skrypt powłoki lub pozyskiwane jako plik rc, skrypt może sprawdzić i porównać $0i $BASH_SOURCEi ustalić, czy exitmożna bezpiecznie stosować.

Oto krótki fragment kodu do tego

[ "X$(basename $0)" = "X$(basename $BASH_SOURCE)" ] && \
    echo "***** executing $name_src as a shell script *****" || \
    echo "..... sourcing $name_src ....."
Tzunghsing David Wong
źródło
-1

1) Wyjście 0 wyjdzie ze skryptu, jeśli się powiedzie.

2) Wyjście 1 wyjdzie ze skryptu, jeśli jest to błąd.

Możesz wypróbować powyższe dwa w oparciu o twoje wymagania.

Teja
źródło