Jak czekać w skrypcie bash na kilka podprocesów odrodzonych z tego skryptu, aby zakończyć i zwrócić kod zakończenia! = 0, gdy którykolwiek z podprocesów kończy się kodem! = 0?
Prosty skrypt:
#!/bin/bash
for i in `seq 0 9`; do
doCalculations $i &
done
wait
Powyższy skrypt będzie czekał na wszystkie 10 odrodzonych podprocesów, ale zawsze da status wyjścia 0 (patrz help wait
). Jak mogę zmodyfikować ten skrypt, aby wykrył status wyjścia spawnowanych podprocesów i zwrócił kod wyjścia 1, gdy którykolwiek z podprocesów zakończy się kodem! = 0?
Czy istnieje na to lepsze rozwiązanie niż zbieranie PID podprocesów, czekanie na nie w kolejności i sumowanie stanów wyjścia?
wait -n
, dostępne w nowoczesnym bashu, aby powrócić dopiero po zakończeniu pierwszego / następnego polecenia.wait -n
ma jeden mały problem: jeśli nie ma już zadań potomnych (inaczej warunek wyścigu), zwraca niezerowy status wyjścia (niepowodzenie), który może być nie do odróżnienia od nieudanego procesu potomnego.Odpowiedzi:
wait
także (opcjonalnie) pobiera PID procesu, na który czeka, i za pomocą $! otrzymasz PID ostatniego polecenia uruchomionego w tle. Zmodyfikuj pętlę, aby przechowywać PID każdego odrodzonego podprocesu w tablicy, a następnie zapętl ponownie, czekając na każdy PID.źródło
for i in $n_procs; do ./procs[${i}] & ; pids[${i}]=$!; done; for pid in ${pids[*]}; do wait $pid; done;
prawda?http://jeremy.zawodny.com/blog/archives/010717.html :
źródło
jobs -p
podaje PID podprocesów, które są w stanie wykonania. Pominie proces, jeśli proces zakończy się przedjobs -p
wywołaniem. Więc jeśli którykolwiek z podprocesów zakończy się wcześniejjobs -p
, status zakończenia tego procesu zostanie utracony.jobs -p
nie podaje PID podprocesów, ale zamiast tego GPID . Wygląda na to, że logika oczekiwania i tak działa, zawsze czeka na grupę, jeśli taka grupa istnieje, i pid, jeśli nie, ale dobrze jest być świadomym ... szczególnie, jeśli ktoś ma się na tym opierać i włączyć coś w rodzaju wysyłania wiadomości do podprocesu, w którym przypadek składnia jest różna w zależności od tego, czy masz PID, czy GPID .. tzn.kill -- -$GPID
vskill $PID
Oto prosty przykład użycia
wait
.Uruchom niektóre procesy:
Następnie poczekaj na nich z
wait
poleceniem:Lub po prostu
wait
(bez argumentów) dla wszystkich.Będzie to czekać na zakończenie wszystkich zadań w tle.
Jeśli
-n
opcja jest podana, czeka na zakończenie następnego zadania i zwraca status wyjścia.Zobacz:
help wait
ihelp jobs
składnia.Minusem jest jednak to, że zwróci to tylko status ostatniego identyfikatora, dlatego należy sprawdzić status każdego podprocesu i zapisać go w zmiennej.
Lub włącz funkcję obliczeń, aby utworzyć jakiś plik w przypadku awarii (pusty lub z dziennikiem błędów), a następnie sprawdź ten plik, jeśli istnieje, np
źródło
sleep 20 && true
isleep 20 && false
- tj .: zamień je na swoje funkcje. Aby zrozumieć&&
i||
uruchomman bash
i wpisz „/” (szukaj), a następnie „^ * Listy” (regex), a następnie wprowadź: człowiek przewinie w dół do opisu&&
i||
||
niepowodzeniem, aby złapać STDERR.wait
Jeśli masz zainstalowany GNU Parallel, możesz:
GNU Parallel da ci kod wyjścia:
0 - Wszystkie zadania uruchomiono bez błędów.
1-253 - Niektóre zadania nie powiodły się. Status wyjścia podaje liczbę nieudanych zadań
254 - Ponad 253 zadań nie powiodło się.
255 - Inny błąd.
Obejrzyj filmy wprowadzające, aby dowiedzieć się więcej: http://pi.dk/1
źródło
doCalculations
jest funkcja zdefiniowana w tym samym skrypcie (chociaż OP nie było jasne na temat tego wymagania). Kiedy próbuję,parallel
mówi/bin/bash: doCalculations: command not found
(mówi to 10 razy wseq 0 9
powyższym przykładzie). Zobacz tutaj obejście.xargs
ma pewne możliwości równoległego uruchamiania zleceń za pomocą-P
opcji. Od tutaj :export -f doCalculations ; seq 0 9 |xargs -P 0 -n 1 -I{} bash -c "doCalculations {}"
. Ograniczeniaxargs
są wyliczone na stronie podręcznika dlaparallel
.doCalculations
opiera się na jakichkolwiek innych wewnętrznych zmiennych skryptowych (niestandardowychPATH
itp.), Prawdopodobnie należy je jawnieexport
edytować przed uruchomieniemparallel
.wget -O - pi.dk/3 | sh
, nie dostaniesz zamieszania. Jeśli twój program pakujący zawiódł dla ciebie sprawy, zachęcam do poruszenia problemu z programem pakującym. Zmienne i funkcje powinny zostać wyeksportowane (export -f) dla GNU Parallel, aby je zobaczyć (patrzman parallel
: gnu.org/software/parallel/... )A może po prostu:
Aktualizacja:
Jak wskazało wielu komentujących, powyższe czeka na zakończenie wszystkich procesów przed kontynuowaniem, ale nie kończy działania i kończy się niepowodzeniem, jeśli jeden z nich zawiedzie, można to zrobić za pomocą następującej modyfikacji sugerowanej przez @Bryan, @SamBrightman i innych :
źródło
for pid in $pids; do wait $pid; done
help wait
- z wieloma identyfikatoramiwait
zwraca kod wyjścia tylko ostatniego, jak powiedział @ vlad-frolov.wait
wywołaniem odpowiedniego? Okazuje się, że nie jest to problem: jeśliwait
w procesie, który już został zakończony,wait
natychmiast wyjdzie ze statusem już zakończonego procesu. (Dziękuję,bash
autorzy!)Oto, co do tej pory wymyśliłem. Chciałbym zobaczyć, jak przerwać komendę uśpienia, jeśli dziecko zakończy działanie, aby nie trzeba było dostosowywać się
WAITALL_DELAY
do jego użycia.źródło
Aby zrównoważyć to ...
Przetłumacz to na to ...
--max-procs
na podstawie tego, ile chcesz równoległości (0
oznacza „wszystkie naraz”).xargs
- ale nie zawsze jest instalowany domyślnie.for
tym przykładzie pętla nie jest absolutnie niezbędna, ponieważecho $i
po prostu regeneruje wyjście$(whatever_list
). Po prostu myślę, że użyciefor
słowa kluczowego sprawia, że trochę łatwiej jest zobaczyć, co się dzieje.Oto uproszczony przykład działania ...
źródło
--max-procs
: Jak uzyskać liczbę procesorów / rdzeni w Linuksie z wiersza poleceń?Nie sądzę, aby było to możliwe dzięki wbudowanej funkcjonalności Bash.
Państwo może dostać powiadomienie, gdy dziecko opuszcza:
Jednak nie ma widocznego sposobu, aby uzyskać status wyjścia dziecka w module obsługi sygnału.
Uzyskanie tego statusu potomnego jest zwykle zadaniem
wait
rodziny funkcji w niższych poziomach interfejsów API POSIX. Niestety wsparcie Bash na to jest ograniczone - możesz poczekać na jeden konkretny proces potomny (i uzyskać jego status wyjścia) lub możesz poczekać na wszystkie z nich i zawsze uzyskać wynik 0.To, co wydaje się niemożliwe, to odpowiednik
waitpid(-1)
, który blokuje, dopóki nie powróci żaden proces potomny.źródło
Widzę tu wiele dobrych przykładów, które też chciałem wrzucić.
Używam czegoś bardzo podobnego do uruchamiania / zatrzymywania serwerów / usług równolegle i sprawdzam status każdego wyjścia. Działa świetnie dla mnie. Mam nadzieję, że to komuś pomoże!
źródło
trap "kill 0" EXIT
Używam tego:
źródło
Poniższy kod będzie czekał na zakończenie wszystkich obliczeń i zwróci status wyjścia 1, jeśli którekolwiek z doCalculations zakończy się niepowodzeniem.
źródło
Po prostu przechowuj wyniki poza powłoką, np. W pliku.
źródło
Oto moja wersja, która działa na wiele pid, rejestruje ostrzeżenia, jeśli wykonanie trwa zbyt długo, i zatrzymuje podprocesy, jeśli wykonanie trwa dłużej niż podana wartość.
Przykład: poczekaj, aż wszystkie trzy procesy zakończą się, zaloguj ostrzeżenie, jeśli wykonanie zajmie więcej czasu niż 5 sekund, zatrzymaj wszystkie procesy, jeśli wykonanie zajmie więcej niż 120 sekund. Nie zamykaj programu w przypadku awarii.
źródło
Jeśli masz wersję bash 4.2 lub nowszą, poniższe mogą być przydatne. Używa tablic asocjacyjnych do przechowywania nazw zadań i ich „kodu”, a także nazw zadań i ich pid. Wbudowałem również prostą metodę ograniczania szybkości, która może się przydać, jeśli twoje zadania zajmują dużo czasu procesora lub wejścia / wyjścia i chcesz ograniczyć liczbę jednoczesnych zadań.
Skrypt uruchamia wszystkie zadania w pierwszej pętli i zużywa wyniki w drugiej.
Jest to trochę przesada w przypadku prostych przypadków, ale pozwala na całkiem porządne rzeczy. Na przykład można przechowywać komunikaty o błędach dla każdego zadania w innej tablicy asocjacyjnej i drukować je po ustąpieniu wszystkiego.
źródło
Właśnie modyfikowałem skrypt w tle i równolegle proces.
Przeprowadziłem kilka eksperymentów (w systemie Solaris zarówno z bash, jak i ksh) i odkryłem, że „czekanie” wyświetla status wyjścia, jeśli nie jest zero, lub listę zadań, które zwracają niezerowe wyjście, gdy nie podano argumentu PID. Na przykład
Grzmotnąć:
Ksh:
Ten wynik jest zapisywany do stderr, więc prostym rozwiązaniem dla przykładu OP może być:
Kiedy to:
zwróci również liczbę, ale bez pliku tmp. Można to również wykorzystać w ten sposób, na przykład:
Ale to nie jest o wiele bardziej przydatne niż plik IMO pliku tmp. Nie mogłem znaleźć użytecznego sposobu na uniknięcie pliku tmp, jednocześnie unikając uruchamiania „czekania” w podpowłoce, co w ogóle nie zadziała.
źródło
Próbowałem tego i połączyłem wszystkie najlepsze części z innych przykładów tutaj. Ten skrypt wykona
checkpids
funkcję po zakończeniu dowolnego procesu w tle i wyśle status wyjścia bez uciekania się do odpytywania.źródło
set -m
pozwala używać fg i bg w skrypciefg
, oprócz umieszczania ostatniego procesu na pierwszym planie, ma ten sam status wyjścia, co proces, który pierwszy planujewhile fg
przestanie zapętlać, gdy którykolwiekfg
z wyjść ma niezerowy status wyjścianiestety nie będzie to obsługiwać przypadku, gdy proces w tle kończy się z niezerowym statusem wyjścia. (pętla nie zakończy się natychmiast. Poczeka na zakończenie poprzednich procesów).
źródło
Jest tu już wiele odpowiedzi, ale jestem zaskoczony, że nikt nie sugerował się używaniem tablic ... Więc oto co zrobiłem - może to być przydatne dla niektórych w przyszłości.
źródło
To działa, powinno być tak samo dobre, jeśli nie lepsze niż odpowiedź @ HoverHell!
i oczywiście unieśmiertelniłem ten skrypt w projekcie NPM, który pozwala na równoległe uruchamianie poleceń bash, przydatnych do testowania:
https://github.com/ORESoftware/generic-subshell
źródło
trap $? $$
zdaje się ustawiać kod wyjścia na 0 i PID na bieżącą działającą powłokę bash, za każdym razem dla mniepułapka jest twoim przyjacielem. Możesz pułapkować na ERR w wielu systemach. Możesz zatrzymać EXIT lub DEBUG, aby wykonać kawałek kodu po każdym poleceniu.
Jest to dodatek do wszystkich standardowych sygnałów.
źródło
U
set -e
góry powoduje zatrzymanie skryptu w przypadku niepowodzenia.expect
powróci,1
jeśli jakiekolwiek subjob zakończy się niepowodzeniem.źródło
Właśnie w tym celu napisałem
bash
funkcję o nazwie:for
.Uwaga :
:for
nie tylko zachowuje i zwraca kod wyjścia funkcji powodującej błąd, ale także kończy wszystkie równoległe działające wystąpienia. Co może nie być potrzebne w tym przypadku.stosowanie
for.sh
:Bibliografia
źródło
Użyłem tego niedawno (dzięki Alnitak):
Stamtąd można łatwo ekstrapolować i mieć wyzwalacz (dotknąć pliku, wysłać sygnał) i zmienić kryteria liczenia (zliczyć dotknięte pliki lub cokolwiek innego), aby odpowiedzieć na to wyzwalanie. Lub jeśli chcesz 'dowolne' niezerowe rc, po prostu zabij blokadę z save_status.
źródło
Potrzebowałem tego, ale proces docelowy nie był potomkiem bieżącej powłoki, w którym to przypadku
wait $PID
nie działa. Zamiast tego znalazłem następującą alternatywę:Zależy to od obecności procfs , które mogą nie być dostępne (na przykład Mac nie zapewnia tego). Aby zapewnić przenośność, możesz użyć tego:
źródło
Wychwytywanie sygnału CHLD może nie działać, ponieważ niektóre sygnały można utracić, jeśli pojawią się jednocześnie.
źródło
rozwiązaniem jest czekanie na kilka podprocesów i wyjście, gdy którekolwiek z nich zakończy się z niezerowym kodem stanu, za pomocą „wait -n”
kod stanu „127” dotyczy nieistniejącego procesu, co oznacza, że dziecko mogło wyjść.
źródło
Poczekaj na wszystkie zadania i zwróć kod zakończenia ostatniego zadania, które się nie powiodło. W przeciwieństwie do powyższych rozwiązań nie wymaga to oszczędzania pid. Po prostu odejdź i czekaj.
źródło
Może zdarzyć się, że proces zostanie zakończony przed oczekiwaniem na proces. Jeśli uruchomimy oczekiwanie na proces, który jest już zakończony, spowoduje to błąd, taki jak pid nie jest potomkiem tej powłoki. Aby uniknąć takich przypadków, można użyć następującej funkcji, aby sprawdzić, czy proces jest zakończony, czy nie:
źródło
Myślę, że najprostszym sposobem równoległego uruchamiania zadań i sprawdzania statusu są pliki tymczasowe. Istnieje już kilka podobnych odpowiedzi (np. Nietzche-jou i mug896).
Powyższy kod nie jest bezpieczny dla wątków. Jeśli obawiasz się, że powyższy kod będzie działał w tym samym czasie co on sam, lepiej użyć bardziej unikalnej nazwy pliku, np. Fail. $$. Ostatni wiersz ma spełniać warunek: „zwróć kod wyjścia 1, gdy którekolwiek z podprocesów zakończy się kodem! = 0?” Wrzuciłem tam dodatkowy wymóg, aby posprzątać. Być może łatwiej byłoby napisać to w następujący sposób:
Oto podobny fragment do zbierania wyników z wielu zadań: tworzę katalog tymczasowy, tworzę wyniki wszystkich zadań podrzędnych w osobnym pliku, a następnie zrzucam je do przeglądu. To naprawdę nie pasuje do pytania - wrzucam to jako bonus:
źródło
Prawie wpadłem w pułapkę używania
jobs -p
do zbierania identyfikatorów PID, co nie działa, jeśli dziecko już wyszło, jak pokazano w skrypcie poniżej. Rozwiązaniem, które wybrałem, było po prostu wywołaniewait -n
N razy, gdzie N jest liczbą dzieci, które mam, które znam zdeterminowane.Wynik:
źródło