Obserwuję dziwne zachowanie podczas używania set -e
( errexit
), set -u
( nounset
) wraz z pułapkami ERR i EXIT. Wydają się powiązane, więc postawienie ich w jednym pytaniu wydaje się rozsądne.
1) set -u
nie uruchamia pułapek ERR
Kod:
#!/bin/bash trap 'echo "ERR (rc: $?)"' ERR set -u echo ${UNSET_VAR}
- Oczekiwany: pułapka ERR zostaje wywołana, RC! = 0
- Faktycznie: pułapka ERR nie jest wywoływana, RC == 1
- Uwaga:
set -e
nie zmienia wyniku
2) Użycie set -eu
kodu wyjścia w pułapce WYJŚCIA wynosi 0 zamiast 1
Kod:
#!/bin/bash trap 'echo "EXIT (rc: $?)"' EXIT set -eu echo ${UNSET_VAR}
- Oczekiwany: wywoływana jest pułapka WYJŚCIA, RC == 1
- Faktycznie: pułapka EXIT jest wywoływana, RC == 0
- Uwaga: Podczas używania
set +e
RC == 1. Pułapka WYJŚCIA zwraca prawidłowe RC, gdy jakiekolwiek inne polecenie zgłosi błąd. - Edycja: Istnieje post SO na ten temat z interesującym komentarzem sugerującym, że może to być związane z używaną wersją Bash. Testowanie tego fragmentu w Bash 4.3.11 daje RC = 1, więc to lepiej. Niestety aktualizacja Bash (z wersji 3.2.51) na wszystkich hostach nie jest w tej chwili możliwa, dlatego musimy wymyślić inne rozwiązanie.
Czy ktoś może wyjaśnić którekolwiek z tych zachowań?
Przeszukiwanie tych tematów nie było bardzo udane, co jest dość zaskakujące, biorąc pod uwagę liczbę postów dotyczących ustawień i pułapek Basha. Jest jednak jeden wątek na forum , ale wniosek jest raczej niezadowalający.
bash
zerwał ze standardem i zaczął umieszczać pułapki w podpowłokach. Pułapka ma być wykonana w tym samym środowisku, z którego nastąpił powrót, alebash
nie zrobiła tego od dłuższego czasu.set -e
iset -u
oba są zaprojektowane specjalnie do zabijania skryptów powłoki. Używanie ich w warunkach, które mogą uruchomić ich aplikację, zabije skryptowaną powłokę. Nie ma poruszanie się, że z wyjątkiem do nie korzystania z nich, a zamiast tego testu dla tych warunkach, gdy mają one zastosowanie w sekwencji kodu. Zasadniczo możesz napisać dobry kod powłoki lub użyćset -eu
.-u
nie wyzwalać pułapki ERR (jest to błąd, więc nie powinien wyzwalać pułapki) lub kod błędu to 0 zamiast 1. ten drugi wydaje się być błędem, który został już naprawiony w późniejszej wersji, więc to tyle. Ale pierwsza część jest dość trudna do zrozumienia, jeśli nie zdajesz sobie sprawy, że błędy w ocenie powłoki (rozszerzenie parametrów) i rzeczywiste błędy w poleceniach wydają się być dwiema różnymi rzeczami. Jeśli chodzi o rozwiązanie, cóż, jak zasugerowałeś, staram się teraz unikać-eu
i sprawdzać ręcznie, kiedy jest to konieczne.(set -u; : $UNSET_VAR)
i podobne. Tego rodzaju rzeczy też mogą być dobre - od&&
czasu do czasu możesz zrzucić wiele rzeczy :(set -e; mkdir dir; cd dir; touch dirfile)
jeśli dostaniesz mój dryf. Po prostu są to kontrolowane konteksty - kiedy ustawisz je jako opcje globalne, tracisz kontrolę i stajesz się kontrolowany. Zazwyczaj są jednak bardziej wydajne rozwiązania.Odpowiedzi:
Od
man bash
:set -u
"@"
i"*"
jako błąd podczas wykonywania ekspansji parametr. Jeśli nastąpi próba rozwinięcia zmiennej lub parametru nieuzbrojonego, powłoka wypisuje komunikat o błędzie i, jeśli nie jest-i
nieaktywna, kończy działanie ze statusem niezerowym.POSIX stwierdza, że w przypadku błędu rozszerzenia , nieinteraktywna powłoka powinna wyjść, gdy rozszerzenie jest powiązane albo ze specjalnym wbudowanym powłoką (które to rozróżnienie
bash
i tak regularnie ignoruje, a więc być może jest nieistotne) lub jakąkolwiek inną użytecznością poza ."${x!y}"
, ponieważ!
nie jest ważne Operator) ; implementacja może traktować je jako błędy składniowe, jeśli jest w stanie je wykryć podczas tokenizacji, a nie podczas ekspansji.Również z
man bash
:trap ... ERR
while
lubuntil
hasła ...if
oświadczeniu ...&&
lub||
z wyjątkiem polecenia następującego po ostatnim&&
lub||
...!
.-e
.Zauważ, że pułapka ERR polega na ocenie powrotu innego polecenia. Ale gdy wystąpi błąd rozszerzenia , nie jest uruchamiane polecenie, aby cokolwiek zwrócić. W twoim przykładzie
echo
nigdy się nie zdarza - ponieważ gdy powłoka ocenia i rozwija argumenty, napotyka-u
zmienną nset, która została określona przez jawną opcję powłoki, aby spowodować natychmiastowe wyjście z bieżącej, skryptowanej powłoki.I tak, pułapka WYJŚCIA , jeśli istnieje, jest wykonywana, a powłoka wychodzi z komunikatem diagnostycznym i kończy stan inny niż 0 - dokładnie tak, jak powinna.
Co do rc: 0 , to spodziewam się, że jest to jakiś błąd specyficzny dla wersji - prawdopodobnie związany z dwoma wyzwalaczami dla EXIT występującymi w tym samym czasie i tym, który otrzymuje kod wyjścia drugiego (który nie powinien wystąpić) . W każdym razie z aktualnym
bash
plikiem binarnym zainstalowanym przezpacman
:Dodałem pierwszy wiersz, abyś mógł zobaczyć, że warunki powłoki są takie same jak dla powłoki skryptowej - nie jest ona interaktywna. Dane wyjściowe to:
Oto kilka istotnych uwag z ostatnich dzienników zmian :
$?
nieprawidłowe ustawianie poleceń asynchronicznych .for
komendach miały niewłaściwy numer linii.trap
łączone w asynchronicznych poleceniach podpowłoki.trap
ładowarki dla tych sygnałów i pozwala większośćtrap
koparki do uruchomienia rekurencyjnie (bieganietrap
koparki podczastrap
obsługi jest wykonywany) .Myślę, że jest to ostatni lub pierwszy, który jest najbardziej odpowiedni - lub być może kombinacja tych dwóch. Program
trap
obsługi jest z natury asynchroniczny, ponieważ jego zadaniem jest oczekiwanie i obsługa sygnałów asynchronicznych . I uruchamiasz dwa jednocześnie za pomocą-eu
i$UNSET_VAR
.Może więc powinieneś po prostu zaktualizować, ale jeśli ci się spodoba, zrobisz to z inną powłoką.
źródło
(Używam bash 4.2.53). W części pierwszej strona podręcznika użytkownika bash mówi tylko: „Błąd standardowy zostanie zapisany do standardowego błędu, a nieinteraktywna powłoka zostanie zamknięta”. Nie oznacza to, że zostanie wywołana pułapka ERR, choć zgadzam się, że byłoby użyteczne, gdyby tak się stało.
Aby być pragmatycznym, jeśli tak naprawdę chcesz bardziej czysto radzić sobie z niezdefiniowanymi zmiennymi, możliwym rozwiązaniem jest umieszczenie większości kodu w funkcji, a następnie wykonanie tej funkcji w podpowłoce i odzyskanie kodu powrotu i wyniku stderr. Oto przykład, w którym „cmd ()” jest funkcją:
Po mojej uderzeniu dostaję
źródło