Ostatnio natknąłem się na takie skrypty:
( set -e ; do-stuff; do-more-stuff; ) || echo failed
Dla mnie to wygląda dobrze, ale to nie działa! set -e
Nie stosuje się, gdy dodasz ||
. Bez tego działa dobrze:
$ ( set -e; false; echo passed; ); echo $?
1
Jednak jeśli dodam ||
, to set -e
jest ignorowane:
$ ( set -e; false; echo passed; ) || echo failed
passed
Użycie prawdziwej, osobnej powłoki działa zgodnie z oczekiwaniami:
$ sh -c 'set -e; false; echo passed;' || echo failed
failed
Próbowałem tego w wielu różnych powłokach (bash, dash, ksh93) i wszystkie zachowują się w ten sam sposób, więc nie jest to błąd. Czy ktoś może to wyjaśnić?
shell
shell-script
Szalony naukowiec
źródło
źródło
||
zewnętrzna podpowłoka wpływa na zachowanie w podpowłoce.(set -e; echo 1; false; echo 2)
z(set -e; echo 1; false; echo 2) || echo 3
Odpowiedzi:
Zgodnie z tym wątkiem jest to zachowanie, które POSIX określa dla używania „
set -e
” w podpowłoce.(Również byłem zaskoczony.)
Po pierwsze zachowanie:
Drugi post zauważa,
W czwartym poście jest trochę więcej, również autorstwa Erica Blake'a,
To zachowanie jest zdecydowanie zaskakujące. Jest to sprzeczne z intuicją: można oczekiwać, że ponowne włączenie
set -e
będzie miało skutek, a otaczający kontekst nie będzie miał precedensu; ponadto sformułowanie standardu POSIX nie wyjaśnia tego szczególnie wyraźnie. Jeśli czytasz to w kontekście, w którym polecenie się nie udaje, reguła nie ma zastosowania: ma zastosowanie tylko w otaczającym kontekście, jednak odnosi się do niej całkowicie.źródło
set -e; (false; echo passed;) || echo failed
. Nie dziwi mnie, że -e jest ignorowane w tym przypadku, biorąc pod uwagę sformułowanie standardu. W moim przypadku jednak jawnie ustawiam -e w podpowłoce i oczekuję , że podpowłoka zakończy działanie po awarii. W podpowłoce nie ma listy AND-OR ...if
i||
i&&
są zaraźliwe? to absurdalneRzeczywiście,
set -e
nie ma żadnego efektu w podpowłokach, jeśli użyjesz||
operatora po nich; np. to nie zadziała:Aaron D. Marasco w swojej odpowiedzi świetnie wyjaśnia, dlaczego tak się zachowuje.
Oto mała sztuczka, której można użyć, aby to naprawić: uruchom wewnętrzne polecenie w tle, a następnie natychmiast poczekaj na to.
wait
Wbudowane zwróci kod wyjścia polecenia wewnętrznej, a teraz używasz||
powait
, a nie funkcji wewnętrznej, więcset -e
działa prawidłowo wewnątrz tego ostatniego:Oto ogólna funkcja oparta na tym pomyśle. Powinien on działać we wszystkich powłokach zgodnych z POSIX, jeśli usuniesz
local
słowa kluczowe, tzn. Zastąpiszlocal x=y
je tylkox=y
:Przykład użycia:
Uruchamianie przykładu:
Jedyną rzeczą, o której musisz wiedzieć podczas korzystania z tej metody jest to, że wszystkie modyfikacje zmiennych Shell wykonane z komendy, którą przekazujesz
run
, nie będą propagowane do funkcji wywołującej, ponieważ komenda działa w podpowłoce.źródło
Nie wykluczyłbym, że to błąd tylko dlatego, że kilka pocisków zachowuje się w ten sposób. ;-)
Mam więcej zabawy do zaoferowania:
Czy mogę zacytować fragment man bash (4.2.24):
Być może ewaluacja kilku poleceń prowadzi do ignorowania || kontekst.
źródło
eval
Sztuczka nie działa dla mnie. Próbowałem bash, bash w trybie posix i dash.set -e
.set -e
jest zepsuty przez projekt. Nie użyłbym tego do niczego poza najprostszymi skryptami powłoki bez struktur kontrolnych, podpowłok lub podstawień poleceń.Obejście problemu przy używaniu najwyższego poziomu
set -e
Doszedłem do tego pytania, ponieważ używałem
set -e
jako metody wykrywania błędów:i bez
||
tego skrypt przestanie działać i nigdy nie osiągniedo_more_stuff
.Ponieważ wydaje się, że nie ma czystego rozwiązania, myślę, że po prostu zrobię proste
set +e
na moich skryptach:źródło