Zgodnie z tym podręcznikiem :
-E (także -o errtrace)
Jeśli jest ustawiony, każda pułapka na ERR jest dziedziczona przez funkcje powłoki, podstawienia poleceń i polecenia wykonywane w środowisku podpowłoki. Pułapka ERR zwykle nie jest dziedziczona w takich przypadkach.
Jednak muszę to interpretować nieprawidłowo, ponieważ następujące działania nie działają:
#!/usr/bin/env bash
# -*- bash -*-
set -e -o pipefail -o errtrace -o functrace
function boom {
echo "err status: $?"
exit $?
}
trap boom ERR
echo $( made up name )
echo " ! should not be reached ! "
Znam już proste przypisanie, my_var=$(made_up_name)
zakończy skrypt za pomocą set -e
(tj. Errexit).
Czy -E/-o errtrace
powinien działać jak powyższy kod? Czy najprawdopodobniej źle to odczytałem?
bash
command-substitution
dgo.a
źródło
źródło
echo $( made up name )
przez$( made up name )
powoduje pożądane zachowanie. Nie mam jednak wyjaśnienia.var=$( pipe )
i$( pipe )
przykłady reprezentowałyby punkty końcowe potoku, podczas gdypipe > echo
nie. Moja strona podręcznika mówi: „1. Niepowodzenie jakiegokolwiek pojedynczego polecenia w potoku wielozadaniowym nie spowoduje wyjścia powłoki. Należy wziąć pod uwagę tylko awarię samego potoku.”echo
zawsze zwraca 0. To musi być uwzględnione w analizie ...Odpowiedzi:
Uwaga:
zsh
narzeka na „złe wzorce”, jeśli nie skonfigurujesz go tak, aby akceptował „komentarze śródliniowe” dla większości przykładów tutaj i nie uruchamiasz ich przez powłokę proxy, jak to zrobiłemsh <<-\CMD
.Ok, więc, jak powiedziałem w powyższych komentarzach, nie wiem konkretnie o bash'ach
set -E
, ale wiem, że powłoki zgodne z POSIX zapewniają prosty sposób testowania wartości, jeśli chcesz:Powyżej zobaczysz, że chociaż
parameter expansion
testowałem${empty?} _test()
wciążreturn
s to przepustka - jak wykazano w poprzednim.echo
Dzieje się tak, ponieważ nieudana wartość zabija$( command substitution )
podpowłokę, która ją zawiera, ale jej powłoka macierzysta -_test
w tym czasie - kontynuuje transport. Iecho
to nie obchodzi - z przyjemnością podaje się tylko, że\newline; echo
to nie jest test.Ale rozważ to:
Ponieważ nakarmiłem
_test()'s
dane wejściowe z wstępnie oszacowanym parametrem wINIT here-document
teraz,_test()
funkcja nawet nie próbuje się uruchomić. Co więcej,sh
skorupa najwyraźniej całkowicie oddaje ducha iecho "this doesnt even print"
nawet nie drukuje.Prawdopodobnie nie tego chcesz.
Dzieje się tak, ponieważ
${var?}
ekspansja parametrów stylu to zaprojektowana tak, aby wychodzićshell
w przypadku braku parametru, działa to tak :Nie będę kopiować / wklejać całego dokumentu, ale jeśli chcesz niepowodzenia dla
set but null
wartości, użyj formularza:Z
:colon
powyższym. Jeśli chcesz, abynull
wartość się powiodła, po prostu pomiń dwukropek. Możesz też to zanegować i zawieść tylko dla ustawionych wartości, jak pokażę za chwilę.Kolejna seria
_test():
Działa to z wszelkiego rodzaju szybkimi testami, ale powyżej zobaczysz, że
_test()
działa od środkapipeline
awarii, aw rzeczywistości jego zawierającacommand list
podpowłoka zawiedzie całkowicie, ponieważ żadne z poleceń w funkcji nie działa ani następująceecho
uruchamia się wcale, , chociaż pokazano również, że można go łatwo przetestować, ponieważecho "now it prints"
teraz drukuje.Diabeł tkwi w szczegółach. W powyższym przypadku powłoka, która wychodzi, nie należy do skryptu
_main | logic | pipeline
ale( subshell in which we ${test?} ) ||
wymagane jest trochę piaskownicy.I może nie być to oczywiste, ale jeśli chcesz przejść tylko w przeciwnym przypadku lub tylko
set=
wartości, jest to również dość proste:Powyższy przykład wykorzystuje wszystkie 4 formy podstawiania parametrów POSIX i ich różne
:colon null
lubnot null
testy. Więcej informacji znajduje się w powyższym linku, a tutaj znowu .I myślę, że powinniśmy też pokazać naszą
_test
funkcję, prawda? Po prostu deklarujemyempty=something
jako parametr naszej funkcji (lub wcześniej):Należy zauważyć, że ta ocena jest samodzielna - nie wymaga żadnego dodatkowego testu, aby zakończyć się niepowodzeniem. Kilka innych przykładów:
I w końcu wracamy do pierwotnego pytania: jak radzić sobie z błędami w
$(command substitution)
podpowłoce?Prawda jest taka - istnieją dwa sposoby, ale żaden nie jest bezpośredni. Istotą problemu jest proces oceny powłoki - rozszerzenia powłoki (w tym$(command substitution)
) następują wcześniej w procesie oceny powłoki niż bieżące wykonywanie poleceń powłoki - wtedy można uchwycić i uwięzić błędy.Problem polega na tym, że zanim bieżąca powłoka ocenia błędy,
$(command substitution)
podpowłoka została już podstawiona - nie ma już żadnych błędów.Więc jakie są dwa sposoby? Albo zrobisz to jawnie w
$(command substitution)
podpowłoce za pomocą testów, tak jak byś bez niego, albo absorbujesz jego wyniki do bieżącej zmiennej powłoki i testujesz jej wartość.Metoda 1:
Metoda 2:
Nie powiedzie się to bez względu na liczbę zmiennych zadeklarowanych w wierszu:
Nasza wartość zwrotna pozostaje stała:
TERAZ TRAP:
źródło
echo "abc" "${v1:?}"
nie wydaje się on działać (abc nigdy nie jest drukowany). Powłoka zwraca 1. Jest to prawdą nawet z komendą lub bez niej ("${v1:?}"
bezpośrednio na cli). Jednak w przypadku skryptu OP wszystko, co było wymagane do wyzwolenia pułapki, to umieszczenie jego zmiennej zmiennej zawierającej podstawienie nieistniejącej komendy w jednym wierszu. W przeciwnym razie zachowanie echa polega na zwróceniu zawsze 0, chyba że zostanie przerwane, tak jak w testach, które wyjaśniłeś.v=$( madeup )
. Nie widzę w tym niebezpiecznego. To tylko zadanie, na przykład osoba źle odczytała poleceniev="$(lss)"
. To błędy. Tak, możesz zweryfikować za pomocą statusu błędu ostatniego polecenia $? - ponieważ jest to polecenie w wierszu (przypisanie bez nazwy polecenia) i nic więcej - nie argument do echa. Dodatkowo tutaj jest uwięziony przez funkcję as! = 0, więc otrzymujesz informację zwrotną dwukrotnie. W przeciwnym razie z pewnością, jak wyjaśnisz, istnieje lepszy sposób na uporządkowanie tego w ramach, ale OP miał 1 pojedynczą linię: echo plus jego nieudane podstawienie. Zastanawiał się nad echem.W twoim skrypcie jest to wykonanie polecenia (
echo $( made up name )
). W bash polecenia są rozdzielone albo ; lub z nową linią . W poleceniu$( made up name )
jest uważany za część polecenia. Nawet jeśli ta część zawiedzie i powróci z błędem, całe polecenie zostanie wykonane pomyślnie, ponieważecho
nie wiem o tym. Gdy polecenie powróci z 0, nie uruchomiono pułapki.Musisz umieścić go w dwóch poleceniach, przypisaniu i echu
źródło
echo $var
nie zawiedzie -$var
jest powiększany do pustego, zanim w ogóleecho
go obejrzy.Wynika to z błędu w bashu. W trakcie zastępowania polecenia w
made
działa (lub nie można go znaleźć) w podpowłoce, ale podpowłoka jest „zoptymalizowana” w taki sposób, że nie używa pułapek z powłoki nadrzędnej. Zostało to naprawione w wersji 4.4.5 :W przypadku wersji bash 4.4.5 lub nowszej powinieneś zobaczyć następujące dane wyjściowe:
Program obsługi pułapek został wywołany zgodnie z oczekiwaniami, a następnie podpowłoka kończy działanie. (
set -e
powoduje jedynie zamknięcie podpowłoki, a nie elementu nadrzędnego, więc komunikat „nie powinien zostać osiągnięty” powinien zostać osiągnięty.)Obejściem starszych wersji jest wymuszenie utworzenia pełnej, niezoptymalizowanej podpowłoki:
Dodatkowe odstępy są wymagane do odróżnienia od rozszerzenia arytmetycznego.
źródło