Chcę, aby moje skrypty powłoki kończyły się niepowodzeniem, ilekroć wykonanie polecenia nimi nie powiedzie się.
Zazwyczaj robię to z:
set -e
set -o pipefail
(zazwyczaj dodaję set -u
też)
Chodzi o to, że żadne z powyższych nie działa z podstawieniem procesu. Ten kod wypisuje „ok” i kończy z kodem powrotu = 0, podczas gdy chciałbym, aby się nie powiódł:
#!/bin/bash -e
set -o pipefail
cat <(false) <(echo ok)
Czy istnieje coś równoważnego z „pipefail” oprócz zamiany procesu? W jakikolwiek inny sposób przekazać do polecenia wynik polecenia, ponieważ były to pliki, ale zgłaszanie błędu za każdym razem, gdy któryś z tych programów zawiedzie?
Rozwiązaniem człowieka biednego byłoby wykrycie, czy te polecenia zapisują do stderr (ale niektóre polecenia zapisują do stderr w udanych scenariuszach).
Innym rozwiązaniem zgodnym z POSIX-em byłoby użycie nazwanych potoków, ale muszę wyliczyć te podstawienia poleceń, które używają procesu, ponieważ oneliniści budują w locie ze skompilowanego kodu, a tworzenie nazwanych potoków skomplikowałoby rzeczy (dodatkowe polecenia, błąd pułapkowania dla usuwanie ich itp.)
źródło
mkfifo pipe; { rm pipe; cat <file; } >pipe
. Że komenda zawiśnie aż czytelnik otwierapipe
ponieważ jest powłoka, która robi toopen()
i tak szybko, jak tam jest czytnik napipe
fs odwołuje się dopipe
ISrm
D”, a następniecat
kopiuje plik_wejściowy się deskryptorze przez powłokę do tej rury. W każdym razie, jeśli chcesz propagować błąd poza procesem sub: <( ! : || kill -2 "$$")
$$
podstawienie nie działa dla mnie, ponieważ podstawienie polecenia nie jest wykonywane, ponieważ polecenie korzystające z podstawienia procesu odbywa się w potoku poleceń, który jest spawnowany z kodu „non-shell” (python). Prawdopodobnie powinienem utworzyć podproces w Pythonie i potokować je programowo.kill -2 0
.Odpowiedzi:
Można obejść ten problem tylko na przykład:
Warstwa wewnętrzna skryptu znajduje się
SIGTERM
d przed wykonaniem drugiego polecenia (other_command
).echo ok
Polecenie jest wykonywane „czasami”: Problemem jest to, że substytucje procesowe są asynchroniczne. Nie ma gwarancji, żekill $$
polecenie zostanie wykonane przed lubecho ok
poleceniem po nim . Jest to kwestia planowania systemów operacyjnych.Rozważmy taki skrypt bash:
Dane wyjściowe tego skryptu mogą być:
Lub:
Możesz spróbować, a po kilku próbach zobaczysz dwa różne zamówienia na wyjściu. W pierwszym skrypt został zakończony, zanim pozostałe dwie
echo
komendy mogły zapisać do deskryptora pliku. W drugimfalse
lubkill
komenda prawdopodobnie zostały zaplanowane poecho
poleceń.Lub być bardziej precyzyjnie: Wywołanie systemowe
signal()
zkill
utillity który wysyła TheSIGTERM
zaplanowano sygnał do procesu muszli (lub została dostarczona) później lub wcześniej niż echowrite()
wywołań systemowych.Jednak skrypt zatrzymuje się, a kod wyjścia nie ma wartości 0. Dlatego powinno rozwiązać problem.
Inne rozwiązanie jest oczywiście użycie do tego nazwanych potoków. Zależy to jednak od tego, jak skomplikowane byłoby wdrożenie nazwanych potoków lub powyższego obejścia.
Bibliografia:
źródło
Dla przypomnienia, nawet jeśli odpowiedzi i komentarze były dobre i pomocne, zakończyłem wdrażanie czegoś nieco innego (miałem pewne ograniczenia dotyczące odbierania sygnałów w procesie nadrzędnym, o których nie wspomniałem w pytaniu)
Zasadniczo skończyłem robić coś takiego:
Następnie sprawdzam plik błędu. Jeśli istnieje, wiem, która komenda nie powiodła się (a zawartość pliku_błędu może być przydatna). Bardziej gadatliwy i zrzędliwy, niż początkowo chciałem, ale mniej kłopotliwy niż tworzenie nazwanych potoków w poleceniu bash jednoliniowym.
źródło
Ten przykład pokazuje, jak używać
kill
razem ztrap
.Ale
kill
nie można przekazać kodu powrotu z procesu podrzędnego do procesu nadrzędnego.źródło
W podobny sposób, jak zaimplementujesz
$PIPESTATUS
/$pipestatus
z powłoką POSIX, która jej nie obsługuje , możesz uzyskać status wyjścia poleceń, przekazując je za pomocą potoku:Co daje:
Możesz też
pipefail
ręcznie zastosować i zaimplementować podstawianie procesów, tak jak w przypadku powłok, które go nie obsługują:źródło