Mam skrypt bash, który uruchamia proces potomny, który od czasu do czasu ulega awarii (właściwie się zawiesza) i bez wyraźnego powodu (zamknięte źródło, więc niewiele mogę z tym zrobić). W rezultacie chciałbym móc uruchomić ten proces na określony czas i zabić go, jeśli nie powrócił on pomyślnie po określonym czasie.
Czy istnieje prosty i solidny sposób na osiągnięcie tego za pomocą basha?
PS: powiedz mi, czy to pytanie lepiej pasuje do błędu serwera lub superużytkownika.
Odpowiedzi:
(Jak widać w: BASH FAQ, wpis nr 68: „Jak uruchomić polecenie i zatrzymać je (przekroczenie limitu czasu) po N sekundach?” )
Jeśli nie przeszkadza pobieranie coś, wykorzystania
timeout
(sudo apt-get install timeout
) i używać go tak: (większość systemów nie jest już zainstalowany inny sposób wykorzystywaćsudo apt-get install coreutils
)Jeśli nie chcesz czegoś pobierać, zrób to, co limit czasu robi wewnętrznie:
W przypadku, gdy chcesz zrobić limit czasu dla dłuższego kodu bash, użyj drugiej opcji jako takiej:
źródło
cmdpid=$BASHPID
nie weźmie pid powłoki wywołującej, ale (pierwsza) podpowłoka, która jest uruchamiana przez()
. To(sleep
... wywołuje drugą podpowłokę w pierwszej podpowłoce, aby czekała 10 sekund w tle i zabijała pierwszą podpowłokę, która po uruchomieniu zabójczego procesu podpowłoki przechodzi do wykonania swojego obciążenia ...timeout
jest częścią coreutils GNU, więc powinien być już zainstalowany we wszystkich systemach GNU.timeout
jest teraz częścią coreutils.lub aby otrzymać kody wyjścia:
źródło
kill -9
przed wypróbowaniem sygnałów, które proces może przetworzyć jako pierwszy.dosmth
zakończy się za 2 sekundy, inny proces zajmie stary pid, a ty zabijesz nowy?źródło
sleep 999
tutaj) często kończy się szybciej niż narzucony sen (sleep 10
)? Co jeśli chcę dać mu szansę do 1 minuty, 5 minut? A co jeśli mam w skrypcie kilka takich przypadków :)Miałem też to pytanie i znalazłem dwie inne bardzo przydatne rzeczy:
Więc używam czegoś takiego w linii poleceń (OSX 10.9):
Ponieważ jest to pętla, dodałem „uśpienie 0.2”, aby procesor był chłodny. ;-)
(BTW: ping i tak jest złym przykładem, po prostu użyłbyś wbudowanej opcji „-t” (timeout).)
źródło
Zakładając, że masz (lub możesz łatwo utworzyć) plik pid do śledzenia pid dziecka, możesz następnie utworzyć skrypt, który sprawdza czas modyfikacji pliku pid i zabija / odradza proces w razie potrzeby. Następnie po prostu umieść skrypt w crontab, aby działał mniej więcej w wymaganym okresie.
Daj mi znać, jeśli potrzebujesz więcej informacji. Jeśli to nie brzmi tak, jakby pasowało do twoich potrzeb, co z początkiem?
źródło
Jednym ze sposobów jest uruchomienie programu w podpowłoce i komunikowanie się z podpowłoką za pośrednictwem nazwanego potoku za pomocą
read
polecenia. W ten sposób możesz sprawdzić status zakończenia uruchomionego procesu i przekazać go z powrotem przez potok.Oto przykład przekroczenia limitu czasu
yes
polecenia po 3 sekundach. Pobiera PID procesu używającegopgrep
(prawdopodobnie działa tylko w systemie Linux). Istnieje również pewien problem z używaniem potoku, ponieważ proces otwierający potok do odczytu zawiesza się, dopóki nie zostanie on również otwarty do zapisu i odwrotnie. Aby zapobiecread
zawieszaniu się polecenia, „zaklinowałem” potok do odczytu z podpowłoką w tle. (Innym sposobem, aby zapobiec zamrożeniu otwierania potoku do odczytu i zapisu, tj.read -t 5 <>finished.pipe
- to jednak może również nie działać, z wyjątkiem Linuksa.)źródło
Oto próba uniknięcia zabicia procesu po jego zakończeniu, co zmniejsza szansę na zabicie innego procesu z tym samym identyfikatorem procesu (chociaż prawdopodobnie niemożliwe jest całkowite uniknięcie tego rodzaju błędu).
Użyj like
run_with_timeout 3 sleep 10000
, który działa,sleep 10000
ale kończy go po 3 sekundach.Jest to podobne do innych odpowiedzi, które wykorzystują proces przekroczenia limitu czasu w tle do zabicia procesu potomnego po opóźnieniu. Myślę, że jest to prawie to samo, co rozszerzona odpowiedź Dana ( https://stackoverflow.com/a/5161274/1351983 ), z wyjątkiem tego, że powłoka limitu czasu nie zostanie zabita, jeśli już się skończyła.
Po zakończeniu tego programu nadal będzie działać kilka długotrwałych procesów „uśpienia”, ale powinny one być nieszkodliwe.
Może to być lepsze rozwiązanie niż moja inna odpowiedź, ponieważ nie używa funkcji nieprzenośnej powłoki
read -t
i nie używapgrep
.źródło
(exec sh -c "$*") &
ish -c "$*" &
? A konkretnie, po co używać tego pierwszego zamiast drugiego?Oto trzecia odpowiedź, którą tu przedstawiłem. Ten obsługuje przerwania sygnału i czyści procesy w tle, gdy
SIGINT
zostanie odebrany. Używa sztuczki$BASHPID
iexec
użytej w górnej odpowiedzi, aby uzyskać PID procesu (w tym przypadku$$
wsh
wywołaniu). Używa FIFO do komunikowania się z podpowłoką odpowiedzialną za zabijanie i czyszczenie. (To jest jak potok w mojej drugiej odpowiedzi , ale posiadanie nazwanego potoku oznacza, że program obsługi sygnału może również pisać do niego.)Starałem się unikać warunków wyścigu tak bardzo, jak tylko mogłem. Jednak jednym źródłem błędu, którego nie mogłem usunąć, jest zakończenie procesu w tym samym czasie, co przekroczenie limitu czasu. Na przykład
run_with_timeout 2 sleep 2
lubrun_with_timeout 0 sleep 0
. U mnie ten ostatni daje błąd:ponieważ próbuje zabić proces, który już sam się zakończył.
źródło