Usare "while true" per mantenere viva una sceneggiatura è una buona idea?


19

Sto solo saltando in Unix da un mondo diverso e volevo sapere se

while true
do
  /someperlscript.pl
done

Lo script perl stesso ha internamente un watcher di cartelle / file che viene eseguito quando i file vengono modificati nella posizione di destinazione.

Questa ( while true) è una buona idea? In caso contrario, qual è un approccio robusto preferito?

TIA

EDIT: poiché questo sembra aver suscitato un discreto interesse, ecco lo scenario completo. Lo script perl stesso guarda una directory usando un watcher di file. Alla ricezione di nuovi file (arrivano tramite rsync), raccoglie il nuovo e lo elabora. Ora i file in arrivo potrebbero essere danneggiati (non chiedere .. provenienti da un raspberry pi), e talvolta il processo potrebbe non essere in grado di gestirlo. Non so esattamente perché, perché non siamo ancora a conoscenza di tutti gli scenari.

MA - se il processo fallisce per qualche motivo, vogliamo che sia attivo e funzionante e gestisca il file successivo, perché il file successivo non è completamente correlato al precedente che potrebbe aver causato l'errore.

Di solito avrei usato una sorta di catch all e racchiuso l'intero codice in modo che non si blocchi MAI. Ma non ero sicuro del perl.

Da quello che ho capito, usare qualcosa come supervisord è un buon approccio per questo.


9
Questo è Linux o UNIX? Se è Linux, potresti voler controllare l' inotifyAPI in modo da poter evitare un ciclo occupato in attesa che i file cambino nella directory di destinazione.
roaima,

Linux, Ubuntu
Abhinav Gujjar,

Se sai che il file è buono prima che lasci il pi, allora potresti semplicemente eseguire un checksum come md5 o sha1 e inviarlo insieme al tuo file. Quindi il destinatario saprà se ha ottenuto un file danneggiato prima di provare a elaborarlo. Se non lo sai, puoi comunque creare una sorta di checksum parziali o di blocco o qualcosa di simile nel tuo file di dati che assicuri l'integrità dei dati, quindi puoi controllare le cose mentre procedi prima di impegnarti in un processo che può fallire. Un'oncia di prevenzione ...
Joe

Risposte:


21

Dipende da quanto velocemente ritorna lo script perl. Se ritorna rapidamente, potresti voler inserire una piccola pausa tra le esecuzioni per evitare il carico della CPU, ad esempio:

while true
do
  /someperlscript.pl
  sleep 1
done

Ciò impedirà anche un errore della CPU se lo script non viene trovato o si blocca immediatamente.

Il ciclo potrebbe anche essere meglio implementato nello script perl stesso per evitare questi problemi.

Modificare:

Come hai scritto, il solo scopo del loop è riavviare lo script perl in caso di crash, un approccio migliore sarebbe implementarlo come un servizio monitorato, ma il modo preciso per farlo dipende dal sistema operativo. Ad esempio: Solaris smf, Linux systemd o un riavvio basato su cron.


10
Potresti fare while sleep 1; do ...per salvare la vera chiamata.
Raphael Ahrens,

4
@RaphaelAhrens In effetti, sebbene ciò cambierebbe leggermente il comportamento iniziale. L'alternatore until ! sleep 1; do ...; donesalverà comunque quella chiamata incorporata durante l'avvio immediato dello script.
jlliagre,

4
Totalmente, sono totalmente d'accordo sul fatto che un ciclo caldo dovrebbe essere evitato con una chiamata di sonno all'interno del ciclo se hai intenzione di farlo. Concordo anche sul fatto che uno script basato sugli eventi (inotify, et al) sarebbe una soluzione migliore. Ma usare un ciclo while non è intrinsecamente malvagio, a meno che non sia infinito e caldo. Penso che il problema più importante stia probabilmente affrontando il motivo per cui lo script perl fallisce e deve essere riavviato, però.
Craig

2
Meglio: sleep 1& /someperlscript.pl; wait.
user23013

2
@EliahKagan Ben individuato. TBH, non uso mai l' untilistruzione, l'ho confusa con un ipotetico do/untilloop che non esiste nella shell.
jlliagre,

13

Le altre risposte sull'utilizzo inotifysono corrette, ma non una risposta a questa domanda.

Un supervisore di processo, come supervisord, upstarto runit, è progettato esattamente per il problema di guardare e riavviare un servizio in caso di arresto anomalo.

La tua distribuzione probabilmente include un supervisore di processo integrato.


11

while trueva bene come una costruzione "loop forever" per tutti gli usi. Come dicono altre risposte, il corpo del loop non dovrebbe essere vuoto o diventare vuoto in virtù del comando all'interno del loop non funzionante.

Se stai usando Linux, potresti voler usare un comando simile inotifywait, che rende il whileciclo molto più semplice:

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

Qui, il inotifywaitcomando resterà in attesa del verificarsi di un evento del filesystem (in questo esempio, quando vengono scritti i file nella directory). A quel punto, esce correttamente e il corpo del loop viene eseguito. Torna quindi ad aspettare di nuovo. Poiché il inotifywaitcomando attende che accada qualcosa nella directory, è molto più efficiente del polling continuo della directory.


`(apt-get o yum) installa inotify-tools` +1 cool!
JJoao,

4

Sposta il tempo 1 nello script perl (seguendo il suggerimento di @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;

1
Questo non risponde ai requisiti di riavvio in caso di arresto anomalo dello script o di interruzione per qualche motivo.
jlliagre,

@jlliagre, grazie per il commento. Nella risposta, non vedo una specifica chiara per il comportamento previsto dopo un incidente o un'uccisione. Questa è chiaramente una domanda interessante e pertinente. Per il momento lo terrò semplice (se la sceneggiatura è morta o è stata uccisa, resta morta :)
JJoao

@jlliagre Ecco a cosa servono le eccezioni. Ma Perl potrebbe non essere la scelta più appropriata in tal caso poiché non supporta il meccanismo di eccezione. Solo una supposizione. Sarebbe meglio portare lo script in un linguaggio che supporti le eccezioni. Dipende tutto da quanto è grande il lavoro di porting, ovviamente.

@Nasha, in Perl, con gestori di segnale, variabili di errore, Tyr :: Tiny, ecc., Possiamo farlo! ... ma - odio la gestione delle eccezioni e il recupero degli errori: non sono mai completi ...
JJoao

1
@JJoao Concordo con te sul fatto che il recupero degli errori è raramente completo. Spetta ovviamente allo sviluppatore coprire tutti i casi possibili. Lo stesso vale per i PLC nel mondo industriale, quindi dopo tutto deve essere del tutto possibile ;-).

3

Quando il tuo script perl è destinato a funzionare continuamente, perché usare la costruzione while? Quando il perl fallisce in vista di qualche grave problema, il nuovo script perl avviato nel frattempo potrebbe bloccarsi altrettanto duramente. Nuovo-e-ancora-e-così via.
se vuoi davvero ricominciare il tuo perl, considera crontab e uno script che controlla prima le istanze in esecuzione. In questo modo lo script verrà avviato anche dopo un riavvio.


2

In generale, non vi è alcun problema nell'utilizzo while truepoiché si tratta di un test minuscolo che viene eseguito solo dopo la chiusura dello script perl. Tieni presente che, a seconda della variante Linux / Unix che stai utilizzando, lo script potrebbe terminare alla disconnessione. In tal caso, considera l'utilizzo del loop in uno script e chiamalo nohupe mettilo in background, ad esnohup myscript &

Se lo script perl termina troppo spesso e causa un carico della CPU, questo carico è responsabile dello script perl e non di while true.

Vedi anche man nohupper i dettagli.


L'unico problema è il potenziale per un hot loop che aumenta l'utilizzo della CPU. Quindi sono totalmente d'accordo nel mettere una chiamata di sonno all'interno del loop per domarla.
Craig

2

Se si desidera gestire un processo, è possibile esaminare un gestore processi per farlo.

Con molti sistemi recenti, è possibile utilizzare systemd per monitorare i processi (che è uno dei vantaggi di systemd rispetto agli script di script classici). Se la vostra distro di scelta non utilizza systemd, si potrebbe usare daemontools o Monit .


monè una semplice alternativa, se l'enorme massa di monit e systemd ti infastidisce tanto quanto me.
Anko,

2

Il whileciclo non è davvero una buona idea. Non c'è scampo lì - corre solo per sempre - staticamente . Qualsiasi numero di cose potrebbe cambiare nell'ambiente e non ne risentirebbe - e questo potrebbe essere negativo.

Ad esempio, se il file eseguibile della shell responsabile di quel whileciclo viene aggiornato, il kernel non sarà in grado di liberare lo spazio su disco per la versione precedente fino a quando lo script non si chiude perché deve mantenere il descrittore per tutto il tempo in cui viene eseguito. E questo è vero per tutti i file che la shell potrebbe avere aperto per qualsiasi motivo per eseguire il ciclo - saranno tutti tenuti aperti da questo whileciclo per tutto il tempo in cui viene eseguito - per sempre.

E se, il cielo lo proibisce, c'è una perdita di memoria in qualsiasi punto della shell che esegue quel loop - anche il più minuto - continuerà semplicemente a perdere - staticamente . Costruirà senza controllo e l'unica risorsa è ucciderlo con forza, quindi ricominciare da capo solo per fare lo stesso in seguito.

Questo non è davvero il modo in cui dovresti impostare un processo in background - almeno, non secondo me. Invece, come penso, dovrebbe esserci un punto di ripristino: un aggiornamento dello script e del suo stato. Il modo più semplice per farlo è con exec. È possibile sostituire il processo corrente con uno nuovo, mantenendo lo stesso PID, ma eseguendo comunque un nuovo processo.

Ad esempio, se lo perlscript deve restituire true dopo aver gestito correttamente una modifica del file in alcune directory monitorate:

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

...o qualcosa di simile. Qualcosa che consente ai macchinari di base dietro il circuito di aggiornarsi di tanto in tanto.


1

Ho valutato alcune di queste risposte, ma istintivamente provo i sentimenti più calorosi per la risposta di @ WalterA. Bene, l'ho fatto fino a quando non ho creato la mia risposta ...

Personalmente, tenderei ad aggiornare lo script Perl in modo che scriva una voce di registro descrittiva sull'errore e invii un avviso a un amministratore.

Se si prevede che lo script Perl continui l'esecuzione, in attesa di eventi di modifica dal file system, perché non funziona?

Se non fallisce, perché ti preoccupi di racchiuderlo in uno script per riavviarlo all'infinito?

Se c'è un problema di configurazione o una dipendenza interrotta che causa l'interruzione dello script Perl, è semplice riavviarlo ripetutamente per far sì che improvvisamente inizi a funzionare.

Conosci la definizione di follia, vero? (facendo sempre la stessa cosa, aspettandosi un risultato diverso). Sto solo dicendo. ;-)


haha- lo so, lo so. ma sai - il mondo reale fa schifo. Comunque - il motivo è che elabora i file e alcuni file potrebbero non essere nemmeno trasmessi correttamente. Non conosciamo ancora la varietà dei motivi per cui potrebbe non riuscire. Vedo il tuo punto sulla registrazione dell'errore, ma avrò ancora bisogno del backup per elaborare il file successivo tramite qualcosa come
Supervord

Se riesci a rilevare le eccezioni, puoi registrarle, quindi continuare a elaborare e persino tornare indietro e occasionalmente riprovare i file non riusciti? Forse il file non riuscito è stato bloccato da un altro utente o processo quando hai provato a elaborarlo, ecc.
Craig
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.