zamknij skrypt powłoki z podpowłoki


30

Rozważ ten fragment:

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if false; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

Zwykle funcwywołanie powoduje zakończenie skryptu, co jest zamierzonym zachowaniem. Jednak jeśli jest wykonywany w podpowłoce, na przykład w

result=`func`

nie wyjdzie ze skryptu. Oznacza to, że kod wywołujący musi za każdym razem sprawdzać status wyjścia funkcji. Czy istnieje sposób, aby tego uniknąć? Czy po to set -ejest?


1
Chcę funkcji „stop”, która wypisuje komunikat do stderr i zatrzymuje skrypt, ale nie zatrzymuje się, gdy funkcja wywołująca stop jest wykonywana w podpowłoce, jak w przykładzie
Ernest AC

2
Oczywiście, ponieważ wychodzi z podpowłoki nie z bieżącej. Wystarczy wywołać funkcję bezpośrednio: func.

1
nie mogę go wywołać bezpośrednio, ponieważ zwraca ciąg znaków, który musi być przechowywany w zmiennej
Ernest AC

1
@ErnestAC Podaj wszystkie szczegóły w oryginalnym pytaniu. Powyższa funkcja nie zwraca łańcucha.

1
@htor Zmieniłem przykład
Ernest AC

Odpowiedzi:


10

Państwo mogłoby zabić oryginalną powłokę ( kill $$) przed wywołaniem exit, a to pewnie dzieło. Ale:

  • wydaje mi się to raczej brzydkie
  • pęknie, jeśli masz tam drugą podpowłokę, tzn. użyj podpowłoki wewnątrz podpowłoki.

Zamiast tego możesz użyć jednego z kilku sposobów przekazania wartości w Bash FAQ . Niestety większość z nich nie jest taka świetna. Być może utknąłeś sprawdzając błędy po każdym wywołaniu funkcji ( -ema wiele problemów ). Albo to, albo przejdź na Perla.


5
Dzięki. Wolałbym jednak przejść na Python.
Ernest AC

2
Jak piszę, to rok 2019. Mówienie komuś, by „przeszedł na Perla”, jest śmieszne. Przepraszam, że jestem kontrowersyjny, ale czy powiedziałbyś komuś sfrustrowanemu „C”, aby przeszedł na Cobol, co jest odpowiednikiem IMO? Jak podkreśla Ernest, Python jest znacznie lepszym wyborem. Wolę Ruby. Tak czy inaczej, wszystko oprócz Perla.
Graham Nicholls

38

Możesz zdecydować, że na przykład status wyjścia 77 oznacza wyjście z dowolnego poziomu podpowłoki i wykonaj

set -E
trap '[ "$?" -ne 77 ] || exit 77' ERR

(
  echo here
  (
    echo there
    (
      exit 12 # not 77, exit only this subshell
    )
    echo ici
    exit 77 # exit all subshells
  )
  echo not here
)
echo not here either

set -Ew połączeniu z ERRpułapkami przypomina trochę ulepszoną wersję set -e, ponieważ pozwala zdefiniować własną obsługę błędów.

W zsh pułapki ERR są dziedziczone automatycznie, więc nie potrzebujesz set -E, możesz również definiować pułapki jako TRAPERR()funkcje i modyfikować je $functions[TRAPERR], np.functions[TRAPERR]="echo was here; $functions[TRAPERR]"


1
Ciekawe rozwiązanie! Wyraźnie bardziej elegancki niż kill $$.

3
Jedną rzeczą, aby zwrócić uwagę na to pułapka nie będzie obsługiwać komendy interpolowane, np echo "$(exit 77)"; scenariusz będzie echo ""
działał

Ciekawe! Czy jest jakieś szczęście na (dość starszym) bashu, który nie ma -E? może musimy uciekać się do zdefiniowania pułapki na sygnale USER i użycia zabicia do tego sygnału? Zrobię też reasearcha ...
Olivier Dulac

Jak się dowiedzieć, gdy nie znajduje się w pułapce podpowłoki, aby zwrócić 1 zamiast 77?
ceving

7

Alternatywnie kill $$, możesz również spróbować kill 0, będzie działać w przypadku zagnieżdżonych podpowłok (wszystkie osoby wywołujące i proces poboczny otrzymają sygnał)… ale wciąż jest brutalny i brzydki.


2
Czy to nie zabiłoby procesu id 0?
Ernest AC

5
To zabije całą grupę procesów. Możesz trafić w rzeczy, których nie chcesz (jeśli na przykład uruchomiłeś jakieś rzeczy w tle).
derobert

2
@ErnestAC patrz strona kill (2), pids ≤0 mają specjalne znaczenie.
derobert

0

Spróbuj tego ...

stop () {
    echo "${1}" 1>&2
    exit 1
}

func () {
    if $1; then
        echo "foo"
    else
        stop "something went wrong"
    fi
}

echo "shell..."
func $1

echo "subshell..."
result=`func $1`

echo "shell..."
echo "result=$result"

Otrzymuję wyniki ...

# test_exitsubshell true
shell...
foo
subshell...
shell...
result=foo
# test_exitsubshell false
shell...
something went wrong

Uwagi

  • Sparametryzowano, aby umożliwić przeprowadzenie iftestu truelub false(patrz 2 przebiegi)
  • Kiedy iftest falsesię kończy, nigdy nie osiągamy podpowłoki.

Jest to bardzo podobne do oryginalnego pomysłu, o którym użytkownik pisał i powiedział, że nie działa. Nie sądzę, że to działa w przypadku podpowłoki. Twój test przy użyciu false kończy się po przypadku „powłoki” i nigdy nie dochodzi do przypadku „podpowłoki”. Sądzę, że w tym przypadku się nie powiedzie, ponieważ podpowłoka wyjdzie z wywołania „exit 1”, ale nie propaguje błędu do zewnętrznej powłoki.
stuckj

0

(Odpowiedź specyficzna dla Bash) Bash nie ma pojęcia wyjątków. Jednak z zestawem -o errexit (lub odpowiednikiem: zestaw -e) na poziomie zewnętrznym nieudane polecenie spowoduje zamknięcie podpowłoki z niezerowym statusem wyjścia. Jeśli jest to zestaw zagnieżdżonych podpowłok bez warunków warunkowych wokół wykonywania tych podpowłok, skutecznie „zwija” cały skrypt i kończy działanie.

Może to być trudne przy próbie włączenia bitów różnych kodów bash do większego skryptu. Jedna porcja bash może działać sama, ale gdy zostanie wykonana w errexit (lub bez errexit), zachowuj się w nieoczekiwany sposób.

[192.168.13.16 (f0f5e19e) ~ 22:58:22] # bash -o errexit / tmp / foo
coś poszło nie tak
[192.168.13.16 (f0f5e19e) ~ 22:58:31] # bash / tmp / foo
coś poszło nie tak
Ale i tak tu dotarliśmy
[192.168.13.16 (f0f5e19e) ~ 22:58:37] # cat / tmp / foo
#! / bin / bash
zatrzymać () {
    echo „$ {1}”
    wyjście 1
}

jeśli fałszywe; następnie
    echo „foo”
jeszcze
    (
        przestań „coś poszło nie tak”
    )
    echo „Ale i tak tu dotarliśmy”
fi
[192.168.13.16 (f0f5e19e) ~ 22:58:40] #

-2

Mój przykład wyjścia w jednej linijce:

COMAND || ( echo "ERROR – executing COMAND, exiting..." ; exit 77 );[ "$?" -eq 77 ] && exit

1
Wydaje się, że nie jest to odpowiedź, która działałaby z poleceniem działającym w podpowłoce zgodnie z żądaniem OP ... To powiedziawszy, chociaż nie zgadzam się z głosami odmawiającymi udzielenia odpowiedzi. Głosy w dół bez komentarza lub powodu są tak samo nieprzydatne jak złe odpowiedzi.
DVS
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.