Dokładny język używany w specyfikacji Single UNIX do opisania znaczeniaset -e
to:
Gdy ta opcja jest włączona, jeśli proste polecenie nie powiedzie się z jednego z powodów wymienionych w Konsekwencjach błędów powłoki lub zwróci wartość statusu wyjścia> 0 i nie jest [poleceniem warunkowym lub negowanym], wówczas powłoka natychmiast się kończy.
Nie wiadomo, co się stanie, gdy takie polecenie pojawi się w podpowłoce . Z praktycznego punktu widzenia wszystko, co może zrobić podpowłoka, to wyjść i zwrócić niezerowy stan powłoki nadrzędnej. To, czy powłoka nadrzędna z kolei zakończy działanie, zależy od tego, czy ten niezerowy status przekłada się na proste polecenie, które zawodzi w powłoce nadrzędnej.
Jednym z takich problematycznych przypadków jest przypadek, który napotkałeś: niezerowy status powrotu z podstawienia polecenia . Ponieważ ten status jest ignorowany, nie powoduje to zamknięcia powłoki nadrzędnej. Jak już odkryłeś , sposobem wzięcia pod uwagę statusu wyjścia jest użycie podstawienia polecenia w prostym przydziale : wtedy stanem wyjścia przypisania jest status wyjścia ostatniego podstawienia polecenia w przypisaniu (-ach) .
Zauważ, że będzie to działało zgodnie z przeznaczeniem tylko wtedy, gdy istnieje podstawienie pojedynczego polecenia, ponieważ brany jest pod uwagę tylko status ostatniego zastąpienia. Na przykład następujące polecenie jest skuteczne (zarówno zgodnie ze standardem, jak i w każdej implementacji, jaką widziałem):
a=$(false)$(echo foo)
Innym przypadkiem jest, aby obserwować wyraźne podpowłok : (somecommand)
. Zgodnie z powyższą interpretacją podpowłoka może zwrócić niezerowy status, ale ponieważ nie jest to proste polecenie w powłoce nadrzędnej, powłoka nadrzędna powinna kontynuować. W rzeczywistości wszystkie znane mi powłoki powodują, że rodzic powraca w tym momencie. Chociaż jest to przydatne w wielu przypadkach, na przykład gdy (cd /some/dir && somecommand)
nawiasy są używane do utrzymania operacji takiej jak bieżąca zmiana katalogu lokalnego, narusza specyfikację, jeśli set -e
jest wyłączona w podpowłoce lub jeśli podpowłoka zwraca niezerowy status w sposób, który nie zakończyłby go, na przykład używając !
prawdziwego polecenia. Na przykład wszystkie programy ash, bash, pdksh, ksh93 i zsh wychodzą bez wyświetlania foo
w następujących przykładach:
set -e; (set +e; false); echo "This should be displayed"
set -e; (! true); echo "This should be displayed"
Jednak żadne proste polecenie nie zadziałało, dopóki nie set -e
działało!
Trzecim problematycznym przypadkiem są elementy niebanalnego rurociągu . W praktyce wszystkie powłoki ignorują awarie elementów rurociągu innych niż ostatni i wykazują jedno z dwóch zachowań dotyczących ostatniego elementu rurociągu:
- ATT ksh i zsh, które wykonują ostatni element potoku w powłoce nadrzędnej, działają w zwykły sposób: jeśli proste polecenie zawiedzie w ostatnim elemencie potoku, powłoka wykonująca to polecenie, która okazuje się być powłoką nadrzędną, wychodzi
- Inne powłoki aproksymują zachowanie, wychodząc, jeśli ostatni element potoku zwraca niezerowy status.
Tak jak poprzednio, wyłączenie set -e
lub użycie negacji w ostatnim elemencie potoku powoduje, że zwraca niezerowy status w sposób, który nie powinien kończyć powłoki; powłoki inne niż ATT ksh i zsh zostaną wtedy zamknięte.
pipefail
Opcja Bash powoduje natychmiastowe wyjście potoku pod, set -e
jeśli którykolwiek z jego elementów zwraca niezerowy status.
Zauważ, że jako kolejna komplikacja, bash wyłącza się set -e
w podpowłokach, chyba że jest w trybie POSIX ( set -o posix
lub znajduje się POSIXLY_CORRECT
w środowisku, gdy zaczyna się bash).
Wszystko to pokazuje, że specyfikacja POSIX niestety źle radzi sobie z określeniem -e
opcji. Na szczęście istniejące powłoki są w większości spójne w swoim zachowaniu.
set -e; (cd /nonexisting)
.(Odpowiadając na własne, ponieważ znalazłem rozwiązanie) Jednym z rozwiązań jest zawsze przypisanie tego do zmiennej pośredniej. W ten sposób
$?
ustawiany jest kod powrotu ( ).Więc
Wyjdzie
1
(lub zamiast tego wyjdzie, jeśliset -e
jest obecny), jednak:Wyjdzie
0
po pustej linii. Kod powrotu echa (lub innego polecenia, które uruchomiono z wyjściem wstecznym) zastąpi kod powrotu 0.Nadal jestem otwarty na rozwiązania, które nie wymagają zmiennej pośredniej, ale to mi trochę pomogło.
źródło
Jak OP wskazał w swojej własnej odpowiedzi, przypisanie wyjścia komendy do zmiennej rozwiązuje problem;
$?
pozostaje nietknięty.Jednak jeden przypadek na krawędzi może nadal łamać fałszywe negatywy (tzn. Polecenie nie powiedzie się, ale błąd się nie pojawi),
local
deklaracja zmiennej:local myvar=$(subcommand)
będzie zawsze wrócić0
!bash(1)
wskazuje to:Oto prosty przypadek testowy:
Wyjście:
źródło
VAR=...
ilocal VAR=...
naprawdę mnie oszołomiła!Jak powiedzieli inni,
local
zawsze zwróci 0. Rozwiązanie polega na zadeklarowaniu zmiennej najpierw:Wynik:
źródło
Aby zakończyć działanie po niepowodzeniu podstawienia polecenia, możesz jawnie ustawić
-e
w podpowłoce:źródło
Ciekawy punkt!
Nigdy się z tym nie natknąłem, ponieważ nie jestem przyjacielem
set -e
(wolę raczejtrap ... ERR
), ale już to przetestowałem:trap ... ERR
nie wychwytywaj też błędów$(...)
(lub staromodnych backticków).Myślę, że problemem jest (jak często) to, że tutaj wywoływana jest podpowłoka i
-e
wyraźnie oznacza bieżącą powłokę.Jedynym innym rozwiązaniem, które przyszło mi do głowy w tym momencie, byłoby użycie odczytu:
To rzuca
ERR
iz-e
pociskiem zostanie zakończone. Jedyny problem: działa to tylko w przypadku poleceń z jednym wierszem danych wyjściowych (lub przepuszczasz coś, co łączy linie).źródło