Ist die Verwendung von "while true", um ein Skript am Leben zu erhalten, eine gute Idee?

19

Ich springe gerade in Unix aus einer anderen Welt und wollte wissen, ob

while true
do
  /someperlscript.pl
done

Das Perl-Skript selbst verfügt intern über eine Ordner- / Dateiüberwachung, die ausgeführt wird, wenn Dateien am Zielspeicherort geändert werden.

Ist das ( while true) eine gute Idee? Wenn nicht, was ist ein bevorzugter robuster Ansatz?

TIA

EDIT: Da dies einiges an Interesse geweckt zu haben scheint, ist hier das komplette Szenario. Das Perl-Skript selbst überwacht ein Verzeichnis mithilfe eines Dateiüberwachungsprogramms. Beim Empfang neuer Dateien (sie kommen über rsync an), nimmt es die neue und verarbeitet sie. Jetzt sind die eingehenden Dateien möglicherweise beschädigt (fragen Sie nicht nach .., die von einem Himbeer-Pi stammen), und manchmal ist der Prozess möglicherweise nicht in der Lage, damit umzugehen. Ich weiß nicht genau warum, da uns noch nicht alle Szenarien bekannt sind.

ABER - wenn der Prozess aus irgendeinem Grund fehlschlägt, soll er aktiv sein und die nächste Datei verarbeiten, da die nächste Datei nicht mit der vorherigen Datei zusammenhängt, die den Fehler verursacht haben könnte.

Normalerweise hätte ich irgendeine Art von catch all benutzt und den gesamten Code darum gewickelt, damit er NIEMALS abstürzt. War mir aber für Perl nicht sicher.

Soweit ich weiß, ist die Verwendung von Supervisord ein guter Ansatz dafür.

Abhinav Gujjar
quelle
9
Ist das Linux oder UNIX? Wenn es sich um Linux handelt, sollten Sie die inotifyAPI überprüfen , um zu vermeiden, dass eine Besetztschleife darauf wartet, dass Dateien in Ihrem Zielverzeichnis geändert werden.
Roaima
Sein Linux, Ubuntu
Abhinav Gujjar
Wenn Sie wissen, dass die Datei gut ist, bevor sie das pi verlässt, können Sie einfach eine Prüfsumme wie md5 oder sha1 ausführen und diese zusammen mit Ihrer Datei senden. Dann weiß der Empfänger, ob eine fehlerhafte Datei vorliegt, bevor er versucht, sie zu verarbeiten. Wenn Sie das nicht wissen, können Sie trotzdem eine Art Teil- oder Blockprüfsummen oder ähnliches in Ihre Datendatei einfügen, um die Datenintegrität zu gewährleisten. So können Sie die Dinge während des Vorgangs überprüfen, bevor Sie sich einem Prozess unterziehen, der fehlschlagen kann. Eine Unze Prävention ...
Joe

Antworten:

21

Das hängt davon ab, wie schnell das Perl-Skript zurückkehrt. Wenn es schnell zurückkehrt, möchten Sie möglicherweise eine kleine Pause zwischen den Ausführungen einfügen, um eine CPU-Auslastung zu vermeiden, z.

while true
do
  /someperlscript.pl
  sleep 1
done

Dies verhindert auch, dass die CPU überlastet wird, wenn das Skript nicht gefunden wird oder sofort abstürzt.

Die Schleife kann auch besser im Perl-Skript selbst implementiert werden, um diese Probleme zu vermeiden.

Bearbeiten:

Da Sie die Schleife geschrieben haben, besteht der einzige Zweck darin, das Perl-Skript neu zu starten, falls es abstürzt. Ein besserer Ansatz wäre, es als überwachten Dienst zu implementieren, aber der genaue Weg, dies zu tun, ist vom Betriebssystem abhängig. Beispiel: Solaris smf, Linux systemd oder ein Cron-basierter Neustarter.

jlliagre
quelle
10
Sie könnten tun while sleep 1; do ..., um den wahren Anruf zu speichern.
Raphael Ahrens
4
@RaphaelAhrens In der Tat, obwohl das das Ausgangsverhalten leicht verändern würde. Die Alternative until ! sleep 1; do ...; donespeichert diesen integrierten Aufruf weiterhin, während das Skript sofort gestartet wird.
Juli
4
Stimmen Sie voll und ganz zu, dass eine Hot-Loop mit einem Sleep-Call in der Schleife vermieden werden sollte, wenn Sie dies tun. Stimmen Sie auch zu, dass ein ereignisgesteuertes Skript (inotify, et al.) Eine bessere Lösung wäre. Die Verwendung einer while-Schleife ist jedoch nicht unbedingt böse, es sei denn, sie ist unendlich und heiß. Ich denke, das wichtigere Problem ist wahrscheinlich, warum das Perl-Skript fehlschlägt und neu gestartet werden muss.
Craig
2
Besser: sleep 1& /someperlscript.pl; wait.
user23013
2
@EliahKagan Gut gesehen. TBH, ich benutze die untilAnweisung nie , ich habe sie mit einer hypothetischen do/untilSchleife verwechselt, die in der Shell nicht existiert.
Juli
13

Die anderen Antworten zur Verwendung inotifysind korrekt, aber keine Antwort auf diese Frage.

Verfahren Wacher wie supervisord, upstartoder runitist genau das Problem entwickelt , beobachten und dem Neustart eines Dienstes , wenn er abstürzt.

Ihre Distribution wird wahrscheinlich mit einem eingebauten Prozess-Supervisor geliefert.

Jacob Krall
quelle
11

while trueeignet sich gut als Allzweckkonstruktion für "Endlosschleifen". Wie andere Antworten sagen, sollte der Körper der Schleife nicht leer sein oder aufgrund des Befehls innerhalb der Schleife, der nicht funktioniert, leer werden.

Wenn Sie Linux verwenden, können Sie einen Befehl wie den folgenden verwenden inotifywait, wodurch die whileSchleife viel einfacher wird:

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

Hier inotifywaitwartet der Befehl, bis ein Dateisystemereignis auftritt (in diesem Beispiel, wenn Dateien in das Verzeichnis geschrieben werden). Zu diesem Zeitpunkt wird es erfolgreich beendet und der Rumpf der Schleife wird ausgeführt. Es geht dann wieder zum Warten zurück. Da der inotifywaitBefehl darauf wartet, dass im Verzeichnis etwas passiert, ist er weitaus effizienter, als das Verzeichnis fortlaufend abzufragen.

Stuart Caie
quelle
`(apt-get oder yum) installiere inotify-tools` +1 cool!
JJoao
4

Verschiebe while 1 in das Perl-Skript (folge dem @roaima-Vorschlag)

#!/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;
Joao
quelle
1
Dies behebt nicht die Neustartanforderung, falls das Skript abstürzt oder aus irgendeinem Grund beendet wird.
Juli
@jlliagre, danke für den Kommentar. In der Antwort sehe ich keine klare Spezifikation für das beabsichtigte Verhalten nach einem Absturz oder Kill. Dies ist eindeutig eine interessante und relevante Frage. Im Moment werde ich es einfach halten (wenn das Drehbuch gestorben ist oder getötet wurde, bleib tot :)
JJoao
@jlliagre Dafür gibt es Ausnahmen. In diesem Fall ist Perl möglicherweise nicht die beste Wahl, da es den Ausnahmemechanismus nicht unterstützt. Nur eine Vermutung. Am besten portieren Sie das Skript in eine Sprache, die Ausnahmen unterstützt. Es kommt natürlich darauf an, wie groß der Portierungsaufwand ist.
@Nasha, In Perl können wir das mit Signalhandlern, Fehlervariablen, Tyr :: Tiny usw. tun! ... aber - Ich hasse Ausnahmebehandlung und Fehlerbehebung: Sie sind nie vollständig ...
Joao
1
@JJoao Ich stimme Ihnen darin zu, dass die Fehlerbehebung selten vollständig ist. Es ist natürlich Sache des Entwicklers, alle möglichen Fälle abzudecken. So ist es bei SPSen in der Industrie, also muss es doch durchaus möglich sein ;-).
3

Wenn Ihr Perl-Skript die ganze Zeit ausgeführt werden soll, warum sollten Sie die while-Konstruktion verwenden? Wenn das Perl aufgrund eines schwerwiegenden Problems ausfällt, stürzt das neue Perl-Skript, das von while gestartet wurde, möglicherweise genauso hart ab. Immer wieder und so weiter.
Wenn Sie wirklich möchten, dass Ihr Perl neu gestartet wird, sollten Sie crontab und ein Skript in Betracht ziehen, das zuerst prüft, ob Instanzen ausgeführt werden. Auf diese Weise wird Ihr Skript auch nach einem Neustart gestartet.

Walter A
quelle
2

Im Allgemeinen ist die Verwendung kein Problem, while trueda es sich um einen winzigen Test handelt, der erst ausgeführt wird, nachdem das Perl-Skript beendet wurde. Beachten Sie, dass das Skript je nach verwendeter Linux / Unix-Variante beim Abmelden möglicherweise beendet wird. In diesem Fall sollten Sie die Schleife in einem Skript verwenden und mit aufrufen nohupund in den Hintergrund stellen, d. Hnohup myscript &

Wenn das Perl-Skript zu oft beendet wird und CPU-Last verursacht, ist diese Last für das Perl-Skript und nicht für das Perl-Skript verantwortlich while true.

Siehe auch man nohupfür weitere Einzelheiten.

Lambert
quelle
Das einzige Problem ist das Potenzial für eine Hot-Loop, die die CPU-Auslastung erhöht. Ich stimme demzufolge voll und ganz zu, einen Schlafanruf in die Schleife zu stecken, um ihn zu zähmen.
Craig am
2

Wenn Sie einen Prozess verwalten möchten, suchen Sie möglicherweise einen Prozessmanager, um dies zu tun.

In vielen neueren Systemen können Sie systemd verwenden , um Ihre Prozesse zu überwachen (was einer der Vorteile von systemd gegenüber klassischen Init-Skripten ist). Wenn Ihre Distribution Ihrer Wahl nicht systemd verwendet, können Sie daemontools oder monit verwenden .

Andrew Aylett
quelle
monist eine einfache Alternative, wenn die lästige Gewalt von Monit und System Sie genauso stört wie mich.
Anko
2

Die whileSchleife ist wirklich keine gute Idee. Es gibt dort kein Entrinnen - es läuft nur für immer - statisch . Eine beliebige Anzahl von Dingen kann sich in der Umgebung ändern und wird nicht beeinträchtigt - und das kann schlimm sein.

Wenn beispielsweise die für diese whileSchleife verantwortliche ausführbare Shell-Datei aktualisiert wird, kann der Kernel den Speicherplatz für die alte Version erst freigeben , wenn das Skript beendet wird, da der Deskriptor so lange beibehalten werden muss, wie er ausgeführt wird. Und das gilt für alle Dateien, die die Shell möglicherweise aus irgendeinem Grund geöffnet hat, um die Schleife auszuführen. Sie werden alle von dieser whileSchleife so lange offengehalten, wie sie ausgeführt wird - für immer.

Und wenn der Himmel es verbietet, dass irgendwo in der Shell ein Speicherleck auftritt, das diese Schleife ausführt - selbst in der kleinsten Minute -, wird sie einfach weiterlecken - statisch . Es wird ungeprüft erstellt und die einzige Möglichkeit besteht darin, es gewaltsam zu töten und es dann erneut zu starten, um später dasselbe zu tun.

Dies ist wirklich nicht die Art und Weise, wie Sie einen Hintergrundprozess einrichten sollten - zumindest meiner Meinung nach nicht. Stattdessen sollte es meines Erachtens einen Reset-Punkt geben - eine Aktualisierung des Skripts und seines Status. Am einfachsten geht das mit exec. Sie können den aktuellen Prozess durch einen neuen ersetzen - unter Beibehaltung der gleichen PID - und dennoch einen neuen Prozess ausführen.

Wenn Ihr perlSkript beispielsweise true zurückgeben soll, nachdem eine Dateiänderung in einem überwachten Verzeichnis erfolgreich durchgeführt wurde:

#!/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

...oder etwas ähnliches. Etwas, das es den grundlegenden Maschinen hinter der Schleife ermöglicht, sich von Zeit zu Zeit zu aktualisieren.

mikeserv
quelle
1

Ich habe einige dieser Antworten positiv bewertet, habe aber instinktiv die wärmsten Gefühle für die Antwort von @ WalterA. Nun, ich habe es getan, bis ich meine eigene Antwort erstellt habe ...

Persönlich würde ich dazu tendieren, das Perl-Skript so zu aktualisieren, dass es einen beschreibenden Protokolleintrag über den Fehler schreibt und eine Warnung an einen Administrator sendet.

Wenn das Perl-Skript weiter ausgeführt werden soll und auf Änderungsereignisse vom Dateisystem gewartet wird, warum schlägt dies fehl?

Wenn es nicht fehlschlägt, warum möchten Sie es in ein Skript einbinden, um es unbegrenzt neu zu starten?

Wenn es ein Konfigurationsproblem oder eine defekte Abhängigkeit gibt, die zum Abbruch des Perl-Skripts führt, wird es wahrscheinlich nicht plötzlich funktionieren, wenn Sie es einfach immer wieder neu starten.

Sie kennen die Definition von Wahnsinn, oder? (Immer wieder dasselbe tun und ein anderes Ergebnis erwarten). Ich sage es nur. ;-)

Craig
quelle
Ich weiß, ich weiß. aber du weißt schon - die reale Welt ist beschissen. Wie auch immer - der Grund ist, dass es Dateien verarbeitet und einige Dateien möglicherweise nicht einmal richtig übertragen werden. Wir wissen noch nicht, aus welchen Gründen dies fehlschlagen kann. Ich sehe Ihren Standpunkt in Bezug auf die Protokollierung des Fehlers, aber ich muss ihn trotzdem sichern, um die nächste Datei über so etwas wie Supervisord
Abhinav Gujjar,
Wenn Sie die Ausnahmen abfangen können, können Sie sie protokollieren, die Verarbeitung fortsetzen und gelegentlich fehlgeschlagene Dateien wiederholen. Möglicherweise wurde die fehlgeschlagene Datei von einem anderen Benutzer gesperrt oder verarbeitet, als Sie versuchten, sie zu verarbeiten usw.
Craig