Czy użycie „while true” do utrzymania skryptu przy życiu to dobry pomysł?

19

Właśnie wskakuję do unixa z innego świata i chciałem wiedzieć, czy

while true
do
  /someperlscript.pl
done

Sam skrypt perla wewnętrznie ma moduł obserwatora folderów / plików, który jest wykonywany, gdy pliki są zmieniane w lokalizacji docelowej.

Czy to ( while true) dobry pomysł? Jeśli nie, jakie jest preferowane solidne podejście?

TIA

EDYCJA: Ponieważ wydaje się, że wzbudziło to spore zainteresowanie, oto kompletny scenariusz. Sam skrypt perla obserwuje katalog za pomocą obserwatora plików. Po otrzymaniu nowych plików (przychodzą przez rsync), pobiera nowy i przetwarza go. Teraz przychodzące pliki mogą być uszkodzone (nie pytaj ... pochodzące z raspberry pi), a czasem proces może nie być w stanie sobie z tym poradzić. Nie wiem dokładnie dlaczego, ponieważ nie jesteśmy jeszcze świadomi wszystkich scenariuszy.

ALE - jeśli z jakiegoś powodu proces się nie powiedzie, chcemy, aby był uruchomiony i zajął się następnym plikiem, ponieważ następny plik jest całkowicie niezwiązany z poprzednim, który mógł spowodować błąd.

Zwykle używałbym jakiegoś typu catch all i owijałem wokół niego cały kod, aby NIGDY się nie zawiesił. Ale Perl nie był pewien.

Z tego, co zrozumiałem, dobrym rozwiązaniem jest użycie czegoś w rodzaju superwizora.

Abhinav Gujjar
źródło
9
Czy to jest Linux czy UNIX? Jeśli jest to Linux, możesz sprawdzić inotifyinterfejs API, aby uniknąć zajętej pętli czekającej na zmianę plików w katalogu docelowym.
roaima
Jego linux, ubuntu
Abhinav Gujjar
Jeśli wiesz, że plik jest dobry, zanim opuści pi, możesz po prostu uruchomić sumę kontrolną, taką jak md5 lub sha1 i wysłać ją wraz ze swoim plikiem. Wtedy odbiorca będzie wiedział, czy dostał zły plik, zanim spróbuje go przetworzyć. Jeśli tego nie wiesz, możesz nadal wbudować w pliku danych cząstkowe lub blokowe sumy kontrolne lub coś podobnego, co zapewnia integralność danych, dzięki czemu możesz sprawdzać różne rzeczy przed zatwierdzeniem procesu, który może się nie powieść. Uncja profilaktyki ...
Joe

Odpowiedzi:

21

To zależy od szybkości powrotu skryptu perla. Jeśli szybko wraca, możesz wstawić krótką pauzę między wykonaniami, aby uniknąć obciążenia procesora, np .:

while true
do
  /someperlscript.pl
  sleep 1
done

Zapobiegnie to także skokowi procesora, jeśli skrypt nie zostanie znaleziony lub natychmiast się zawiesi.

Pętla może być lepiej zaimplementowana w samym skrypcie perl, aby uniknąć tych problemów.

Edytować:

Ponieważ napisałeś, że jedynym celem pętli jest ponowne uruchomienie skryptu perl w przypadku awarii, lepszym rozwiązaniem byłoby zaimplementowanie go jako usługi monitorowanej, ale dokładny sposób wykonania zależy od systemu operacyjnego. Np .: Solaris smf, Linux systemd lub restart oparty na cronie.

jlliagre
źródło
10
Możesz zrobić, while sleep 1; do ...aby zapisać prawdziwe połączenie.
Raphael Ahrens
4
@RaphaelAhrens Rzeczywiście, choć nieznacznie zmieniłoby to początkowe zachowanie. Alternator until ! sleep 1; do ...; donenadal zapisze to wbudowane wywołanie podczas natychmiastowego uruchamiania skryptu.
jlliagre
4
Całkowicie zgadzam się całkowicie, że należy unikać gorącej pętli z wywołaniem snu w pętli, jeśli zamierzasz to zrobić. Zgadzam się również, że skrypt sterowany zdarzeniami (inotify i in.) Byłby lepszym rozwiązaniem. Ale używanie pętli while nie jest z natury złe, chyba że jest nieskończone i gorące. Myślę, że najważniejszym problemem jest prawdopodobnie to, dlaczego skrypt Perla zawodzi i trzeba go zrestartować.
Craig,
2
Lepiej: sleep 1& /someperlscript.pl; wait.
user23013
2
@EliahKagan Dobrze zauważony. TBH, nigdy nie używam untilinstrukcji, pomyliłem ją z hipotetyczną do/untilpętlą, która nie istnieje w powłoce.
jlliagre,
13

Pozostałe odpowiedzi dotyczące używania inotifysą poprawne, ale nie stanowią odpowiedzi na to pytanie.

Osoba nadzorująca proces, taka jak supervisord, upstartlub runit, została zaprojektowana właśnie w celu obejrzenia i ponownego uruchomienia usługi, jeśli ulegnie awarii.

Twoja dystrybucja prawdopodobnie ma wbudowanego nadzorcę procesu.

Jacob Krall
źródło
11

while truejest w porządku jako uniwersalna konstrukcja „na zawsze”. Jak mówią inne odpowiedzi, ciało pętli nie powinno być puste, ani stać się puste na mocy niedziałającego polecenia wewnątrz pętli.

Jeśli używasz Linuksa, możesz chcieć użyć polecenia podobnego inotifywait, co whileznacznie upraszcza pętlę:

while inotifywait -qqe modify "$DIRECTORY"
do
    process_the_directory "$DIRECTORY"
done

Tutaj inotifywaitpolecenie usiądzie i zaczeka na zdarzenie systemu plików (w tym przykładzie, gdy pliki w katalogu są zapisywane). W tym momencie kończy się powodzeniem i wykonuje się ciało pętli. Następnie wraca do oczekiwania. Ponieważ inotifywaitpolecenie czeka, aż coś się wydarzy w katalogu, jest znacznie wydajniejsze niż ciągłe odpytywanie katalogu.

Stuart Caie
źródło
`(apt-get lub yum) zainstaluj inotify-tools` +1 fajne!
JJoao,
4

Przenieś while while do skryptu perl (zgodnie z sugestią @roaima)

#!/usr/bin/perl

 use Linux::Inotify2;

 my $inotify = new Linux::Inotify2 or die "unable to inotify: $!";

 $inotify->watch ("Dir", IN_MODIFY, ## or in_{acess,create,open, etc...}
   sub { my $e = shift;
     my $name = $e->fullname;
     ## whatever 
     print "$name was modified\n" if $e->IN_MODIFY;
  });

 1 while $inotify->poll;
JJoao
źródło
1
Nie dotyczy to wymogu ponownego uruchomienia, jeśli skrypt ulegnie awarii lub zostanie z jakiegoś powodu zabity.
jlliagre
@jlliagre, dziękuję za komentarz. W odpowiedzi nie widzę jasnej specyfikacji zamierzonego zachowania po awarii lub zabiciu. To wyraźnie interesujące i istotne pytanie. Na razie postaram się to
uprościć
@jlliagre Do tego służą wyjątki. Ale Perl może nie być najlepszym wyborem w takim przypadku, ponieważ nie obsługuje mechanizmu wyjątku. Tylko zgadnij. Najlepiej byłoby przenieść skrypt na język obsługujący wyjątki. Wszystko zależy oczywiście od wielkości pracy związanej z portowaniem.
@Nasha, w Perlu, z programami obsługi sygnałów, zmiennymi błędów, Tyr :: Tiny itp., Możemy to zrobić! ... ale - Nienawidzę obsługi wyjątków i odzyskiwania błędów: nigdy nie są kompletne ...
JJoao 30'15
1
@JJoao Zgadzam się z Tobą w sprawie faktów, że odzyskiwanie po błędzie rzadko kończy się. Oczywiście do dewelopera należy pokrycie wszystkich możliwych przypadków. Podobnie jest ze sterownikami PLC w świecie przemysłu, dlatego musi to być całkiem możliwe ;-).
3

Jeśli twój skrypt w Perlu ma być cały czas uruchomiony, po co korzystać z konstrukcji while? Gdy perl nie powiedzie się z powodu poważnego problemu, nowy skrypt perla uruchomiony przez chwilę może ulec awarii równie mocno. Znowu i znowu i tak dalej.
jeśli naprawdę chcesz, aby twój perl zaczął od nowa, rozważ crontab i skrypt, który najpierw sprawdza, czy działają instancje. W ten sposób skrypt zostanie uruchomiony nawet po ponownym uruchomieniu.

Walter A.
źródło
2

Zasadniczo nie ma problemu z używaniem, while trueponieważ jest to niewielki test, który jest wykonywany dopiero po zakończeniu skryptu perl. Pamiętaj, że w zależności od używanego wariantu Linux / Unix skrypt może zostać zakończony podczas wylogowywania. W takim przypadku rozważ użycie pętli w skrypcie i wywołaj ją za pomocą nohupi umieść w tle, tjnohup myscript &

Jeśli skrypt Perla kończy się zbyt często i powoduje obciążenie procesora, obciążenie to odpowiada skryptowi perl, a nie while true.

Zobacz także man nohupszczegóły.

Lambert
źródło
Jedynym problemem jest potencjał gorącej pętli, która zwiększa wykorzystanie procesora. Więc całkowicie zgadzam się z umieszczeniem połączenia snu w pętli, aby go oswoić.
Craig,
2

Jeśli chcesz zarządzać procesem, możesz zajrzeć do menedżera procesów.

W wielu najnowszych systemach możesz używać systemd do monitorowania procesów (co jest jedną z zalet systemd nad klasycznymi skryptami inicjującymi). Jeśli twoja dystrybucja nie używa systemd, możesz użyć daemontools lub monit .

Andrew Aylett
źródło
monjest prostą alternatywą, jeśli ogromna ilość monitorów i systemowych przeszkadza ci tak samo, jak mnie.
Anko
2

whilePętla nie jest to dobry pomysł. Tam nie ma ucieczki - po prostu działa wiecznie - statycznie . Dowolna liczba rzeczy może się zmienić w środowisku i nie będzie to miało wpływu - i może być źle.

Na przykład, jeśli plik wykonywalny powłoki odpowiedzialny za tę whilepętlę zostanie zaktualizowany, jądro nie będzie w stanie zwolnić miejsca na dysku dla starej wersji, dopóki skrypt nie zostanie zamknięty, ponieważ musi on utrzymywać deskryptor tak długo, jak długo działa. Dotyczy to wszystkich plików, które powłoka mogłaby otworzyć z jakiegokolwiek powodu, aby uruchomić pętlę - wszystkie będą utrzymywane przez tę whilepętlę tak długo, jak długo będzie działać - na zawsze.

A jeśli, nie daj Boże, w dowolnym miejscu powłoki działa przeciek pamięci - nawet przez najdłuższą chwilę - po prostu nadal będzie przeciekać - statycznie . Zbuduje się niezaznaczone, a jedynym sposobem na to jest zabicie go z użyciem siły, a następnie uruchomienie go od nowa, aby zrobić to samo później.

To naprawdę nie jest sposób, w jaki powinieneś konfigurować proces w tle - przynajmniej, moim zdaniem, nie. Zamiast tego, jak myślę, powinien istnieć punkt zerowania - odświeżenie skryptu i jego stanu. Najłatwiej to zrobić za pomocą exec. Możesz zastąpić bieżący proces nowym - zachowując ten sam PID - ale nadal uruchamiając nowy proces.

Na przykład, jeśli perlskrypt powinien zwrócić wartość true po tym, jak pomyślnie obsłużył modyfikację pliku w pewnym monitorowanym katalogu:

#!/bin/sh
trap 'rm -rf -- "${ldir%%*.}"' 0 INT
_exec() case    $#      in
        (0)     exec env -  "PID=$$" "ldir=${TMPDIR:-/tmp}/." \
                            "$0" "$@";;
        (*)     export "$@" "boff=0" "lmt=30"
                exec        "$0" "$@";;
        esac
[ "$PID" = "$$" ] || _exec
[ -w "$ldir" ]  &&
case    $ldir   in
(*.)    until   mkdir -- "$ldir"
        do      :& ldir=$ldir$$$!
        done    2>/dev/null
;;
(*)     until   /someperlscript.pl ||
                [ "$((boff+=1))"  -ge "$lmt" ]
        do      [ -d "$ldir" ]     &&
                sleep "$boff"      || ! break
        done
;;esac  &&      _exec ldir PID

...czy jakoś tak. Coś, co pozwala podstawowym maszynom znajdującym się za pętlą odświeżyć się co jakiś czas.

mikeserv
źródło
1

Głosowałem za kilkoma z tych odpowiedzi, ale instynktownie mam najcieplejsze uczucia do odpowiedzi @ WalterA. Tak było, dopóki nie stworzyłem własnej odpowiedzi ...

Osobiście staram się aktualizować skrypt Perla, aby zapisywał opisowy wpis dziennika o awarii i wysyłał ostrzeżenie do administratora.

Jeśli skrypt Perla ma nadal działać, czekając na zdarzenia zmian z systemu plików, dlaczego nie działa?

Jeśli to nie zawiedzie, dlaczego martwisz się o zawinięcie go w skrypt, aby uruchomić go ponownie w nieskończoność?

Jeśli wystąpi problem z konfiguracją lub zepsuta zależność, która powoduje przerwanie działania skryptu Perl, ponowne uruchomienie go w kółko prawdopodobnie nie spowoduje, że nagle zacznie działać.

Znasz definicję szaleństwa, prawda? (robienie tego samego w kółko, spodziewając się innego rezultatu). Tylko mówię. ;-)

Craig
źródło
haha- Wiem, wiem. ale wiesz - prawdziwy świat jest do bani. W każdym razie - powodem jest to, że przetwarza pliki, a niektóre pliki mogą nawet nie zostać poprawnie przesłane. Nie znamy jeszcze różnych przyczyn niepowodzenia. Rozumiem twój punkt widzenia na temat rejestrowania błędu, ale nadal będę potrzebować kopii zapasowej, aby przetworzyć następny plik za pomocą czegoś w rodzaju superwizora
Abhinav Gujjar
Jeśli potrafisz wychwycić wyjątki, możesz je zarejestrować, a następnie kontynuować przetwarzanie, a nawet wrócić i od czasu do czasu spróbować ponownie nieudanych plików? Być może uszkodzony plik został zablokowany przez innego użytkownika lub proces podczas próby przetworzenia itp.
Craig