Ostatnio mam dziwne problemy z bash. Próbując uprościć mój skrypt, wymyśliłem ten mały fragment kodu:
$ o(){ echo | while read -r; do return 0; done; echo $?;}; o
0
$ o(){ echo | while read -r; do return 1; done; echo $?;}; o
1
return
powinien był wyjść z funkcji bez drukowania $?
, prawda? Cóż, wtedy sprawdziłem, czy mogę sam wrócić z rury:
$ echo | while read -r; do return 1; done
bash: return: can only `return' from a function or sourced script
To samo dzieje się bez while
pętli:
$ foo(){ : | return 1; echo "This should not be printed.";}
$ foo
This should not be printed.
Czy czegoś tu brakuje? Wyszukiwarka Google nic nie przyniosła! Moja wersja bash to 4.2.37 (1) - wydanie na Debian Wheezy.
bash
shell-script
pipe
subshell
Teresa e Junior
źródło
źródło
while
nie są one potrzebne do reprodukcji? Odwraca uwagę od rzeczy.while
Pętla jest bardzo powszechnym zastosowaniem dla potoku zreturn
. Drugi przykład jest bardziej bezpośredni do rzeczy, ale nie sądzę, aby ktokolwiek kiedykolwiek użyłby ...Odpowiedzi:
Powiązane: /programming//a/7804208/4937930
To nie jest błąd, że nie można wyjść ze skryptu lub powrócić z funkcji przez
exit
lubreturn
w podpowłoce. Są one wykonywane w innym procesie i nie wpływają na proces główny.Poza tym, przypuszczam, że widzisz nieudokumentowane zachowania bash na (prawdopodobnie) nieokreślonej specyfikacji. W funkcji nie są zgłaszane żadne błędy
return
na najwyższym poziomie poleceń podpowłoki i po prostu zachowuje się jakexit
.IMHO jest błędem bashowym za niespójne zachowanie polegające
return
na tym, czy główna instrukcja jest w funkcji, czy nie.Wynik:
źródło
return
nie działa z sekwencji poleceń najwyższego poziomu w podpowłoce, a w szczególności nie wychodzi z podpowłoki, jest tym, czego już oczekiwałem od istniejących dokumentów. OP może użyćexit 1 || return 1
tam, gdzie próbują użyćreturn
, a następnie powinien uzyskać oczekiwane zachowanie. EDYCJA: Odpowiedź @ herbert wskazuje, że najwyższy poziomreturn
w podpowłoce działa jakoexit
(ale tylko z podpowłoki).return
w prostej sekwencji podpowłoki należy w każdym przypadku stwierdzić , że jest to błąd czasu wykonywania , ale tak naprawdę nie jest to przypadek , w którym występuje on. Problem ten został również omówiony w gnu.bash.bug , ale nie ma żadnych wniosków.return
instrukcja działa, a zatem jest legalna. Wynikowe zachowanie jest jednak nieokreślone.To nie jest błąd,
bash
ale jego udokumentowane zachowanie :return
Instrukcja jest ważna istota wewnątrz definicji funkcji, ale jest w podpowłoce, jak również, że nie ma wpływu na jego powłoki macierzystej więc następnej instrukcji,echo
jest realizowane niezależnie. Jest to jednak nieprzenośna konstrukcja powłoki, ponieważ standard POSIX pozwala na wykonywanie poleceń tworzących potok w podpowłoce (domyślnie) lub w górnej (dozwolone rozszerzenie).Mamy nadzieję, że możesz powiedzieć,
bash
aby zachowywać się tak, jak oczekujesz, za pomocą kilku opcji:źródło
return
nie wyjdzie z funkcji, czy nie ma większego sensu, gdyby powłoka właśnie wydrukowałabash: return: can only `return' from a function or sourced script
, zamiast dać użytkownikowi fałszywe poczucie, że funkcja mogła zwrócić?return
z podpowłoki. W końcu wie, że jest w podpowłoce, o czym świadczy$BASH_SUBSHELL
zmienna. Największym problemem jest to, że może to prowadzić do fałszywych trafień; użytkownik, który rozumie, jak działa podpowłoki, mógł napisać skrypty, którereturn
zamiast tegoexit
kończą podpowłokę. (I oczywiście istnieją uzasadnione przypadki, w których można chcieć ustawić zmienne lub wykonać acd
w podpowłoce.)return
wraca z podpowłoki zamiast zawieść, ponieważ znajduje się w rzeczywistej funkcji. Problem polega na tym, żehelp return
konkretnie stwierdza:Causes a function or sourced script to exit with the return value specified by N.
Po przeczytaniu dokumentacji każdy użytkownik spodziewałby się, że przynajmniej zawiedzie lub wydrukuje ostrzeżenie, ale nigdy się tak nie zachowaexit
.return
podpowłoki w funkcji z funkcji (w głównym procesie powłoki), nie rozumie zbyt dobrze podpowłoki. I odwrotnie, spodziewałbym się, że czytelnik, który rozumie podpowłoki, oczekujereturn
w podpowłoce w funkcji, aby zakończyć podpowłokę, tak jak toexit
zrobiłoby.Zgodnie z dokumentacją POSIX, używanie
return
poza funkcją lub skryptem źródłowym jest nieokreślone . To zależy od twojej powłoki do obsługi.Powłoka SystemV zgłosi błąd, podczas gdy wewnątrz
ksh
,return
poza funkcją lub skryptem źródłowym zachowują się jakexit
. Większość innych powłok POSIX i osh Schily'ego zachowuje się tak:ksh
izsh
nie wyprowadzał danych, ponieważ ostatnia część potoku w tych powłokach została wykonana w bieżącej powłoce zamiast podpowłoki. Instrukcja return wpłynęła na bieżące środowisko powłoki, które wywołało funkcję, powoduje natychmiastowe zwrócenie funkcji bez drukowania niczego.W sesji interaktywnej
bash
zgłoś tylko błąd, ale nie zakończył powłoki,schily's osh
zgłosił błąd i zakończył działanie powłoki:(
zsh
W sesji interaktywnej i wyjście to zrób terminal nie rozwiązanabash
,yash
ischily's osh
zgłosił błąd, ale nie zakończyć powłoki)źródło
return
jest tutaj używany w funkcji.return
było użycie wewnątrz podpowłoce wewnątrz funkcji , z wyjątkiemksh
izsh
.Myślę, że masz oczekiwane zachowanie, w skrócie, każde polecenie w potoku jest wykonywane w podpowłoce. Możesz przekonać się, próbując zmodyfikować zmienną globalną swojej funkcji:
Nawiasem mówiąc, powrót działa, ale wraca z podpowłoki. Ponownie możesz sprawdzić, czy:
Wyprowadzi następujące:
Tak więc instrukcja return poprawnie opuściła podpowłokę
.
źródło
foo(){ : | return 1 || return 2; echo$?; echo "This should not be printed.";}; foo; echo $?
a otrzymasz wynik2
. Ale dla jasności zrobiłbymreturn 1
toexit 1
.Bardziej ogólną odpowiedzią jest to, że bash i niektóre inne powłoki zwykle umieszczają wszystkie elementy potoku w osobnych procesach. Jest to uzasadnione, gdy wiersz poleceń to
ponieważ programy i tak są zwykle uruchamiane w oddzielnych procesach (chyba że mówisz ). Ale może to być zaskoczeniem
exec program
gdzie niektóre lub wszystkie polecenia są wbudowanymi poleceniami. Trywialne przykłady obejmują:
Nieco bardziej realistycznym przykładem jest
gdzie cała
while
...do
...done
pętla wkłada się podproces, a więc jego zmiant
nie są widoczne na głównej powłoce po zakończeniu pętli. I to jest dokładnie to, co robisz - pipowanie wwhile
pętli, powodowanie, że pętla działa jako podpowłoka, a następnie próba powrotu z podpowłoki.źródło