¿Usar "while true" para mantener vivo un script es una buena idea?

19

Solo estoy saltando a Unix desde un mundo diferente, y quería saber si

while true
do
  /someperlscript.pl
done

El script perl en sí tiene internamente una carpeta / observador de archivos que se ejecuta cuando los archivos se cambian en la ubicación de destino.

¿Es esta ( while true) una buena idea? Si no, ¿cuál es un enfoque robusto preferido?

TIA

EDITAR: Dado que esto parece haber generado un poco de interés, aquí está el escenario completo. El script perl mismo mira un directorio usando un observador de archivos. Al recibir nuevos archivos (llegan a través de rsync), recoge el nuevo y lo procesa. Ahora los archivos entrantes pueden estar corruptos (no pregunte ... provenientes de una frambuesa pi), y a veces el proceso puede no ser capaz de manejarlo. No sé exactamente por qué, porque todavía no conocemos todos los escenarios.

PERO: si el proceso falla por algún motivo, queremos que esté en funcionamiento y que se ocupe del siguiente archivo, porque el siguiente archivo no tiene ninguna relación con el anterior que podría haber causado el error.

Por lo general, habría usado algún tipo de captura de todo y envuelto todo el código a su alrededor para que NUNCA se bloquee. Pero no estaba seguro de Perl.

Por lo que he entendido, usar algo como supervisión es un buen enfoque para esto.

Abhinav Gujjar
fuente
99
¿Es esto Linux o UNIX? Si se trata de Linux, es posible que desee comprobar la inotifyAPI para evitar un bucle ocupado esperando que los archivos cambien en su directorio de destino.
roaima
Its linux, ubuntu
Abhinav Gujjar
Si sabe que el archivo es bueno antes de que salga del pi, entonces podría ejecutar una suma de verificación como md5 o sha1 y enviarla junto con su archivo. Entonces el receptor sabrá si tiene un archivo incorrecto antes de intentar procesarlo. Si no lo sabe, aún puede construir algún tipo de suma de verificación parcial o de bloque o algo similar en su archivo de datos que asegure la integridad de los datos, para que pueda verificar las cosas a medida que avanza antes de comprometerse con un proceso que puede fallar. Una onza de prevención ...
Joe

Respuestas:

21

Eso depende de qué tan rápido regrese el script perl. Si regresa rápidamente, es posible que desee insertar una pequeña pausa entre ejecuciones para evitar la carga de la CPU, por ejemplo:

while true
do
  /someperlscript.pl
  sleep 1
done

Esto también evitará un problema de CPU si el script no se encuentra o se bloquea de inmediato.

El bucle también podría implementarse mejor en el script perl para evitar estos problemas.

Editar:

Como escribió, el único propósito del bucle es reiniciar el script perl en caso de que falle, un mejor enfoque sería implementarlo como un servicio monitoreado, pero la forma precisa de hacerlo depende del sistema operativo. Por ejemplo: Solaris smf, Linux systemd o un reiniciador basado en cron.

jlliagre
fuente
10
Podrías hacer while sleep 1; do ...para guardar la verdadera llamada.
Raphael Ahrens
44
@RaphaelAhrens De hecho, aunque eso cambiaría ligeramente el comportamiento inicial. La alternativa until ! sleep 1; do ...; doneaún guardará esa llamada incorporada al iniciar el script de inmediato.
jlliagre
44
Totalmente, totalmente de acuerdo en que debe evitarse un bucle activo con una llamada de suspensión dentro del bucle si va a hacer esto. También acuerde que una secuencia de comandos controlada por eventos (inotify, et al) sería una mejor solución. Pero usar un ciclo while no es intrínsecamente malo, necesariamente, a menos que sea infinito y caliente. Sin embargo, creo que el problema más importante es tratar de por qué el script perl falla y debe reiniciarse.
Craig
2
Mejor: sleep 1& /someperlscript.pl; wait.
user23013
2
@EliahKagan Bien visto. TBH, nunca uso la untilinstrucción, la he confundido con un do/untilbucle hipotético que no existe en el shell.
jlliagre
13

Las otras respuestas, sobre el uso inotify, son correctas, pero no una respuesta a esta pregunta.

Un supervisor de proceso, como supervisord, upstarto runit, está diseñado para el problema exacto de ver y reiniciar un servicio si falla.

Su distribución probablemente viene con un supervisor de procesos incorporado.

Jacob Krall
fuente
11

while trueestá bien como una construcción de "ciclo para siempre" de uso general. Como dicen otras respuestas, el cuerpo del ciclo no debe estar vacío, o volverse vacío en virtud de que el comando dentro del ciclo no funciona.

Si está utilizando Linux, es posible que desee utilizar un comando como inotifywait, que hace que el whilebucle sea mucho más simple:

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

Aquí, el inotifywaitcomando se sentará y esperará a que ocurra un evento del sistema de archivos (en este ejemplo, cuando se escriben los archivos en el directorio). En ese punto, sale con éxito y se ejecuta el cuerpo del bucle. Luego vuelve a esperar de nuevo. Debido a que el inotifywaitcomando espera que ocurra algo en el directorio, es mucho más eficiente que sondear continuamente el directorio.

Stuart Caie
fuente
`(apt-get o yum) instala inotify-tools` +1 genial!
JJoao
4

Mueva el while 1 al script perl (Siguiendo la sugerencia de @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
fuente
1
Esto no aborda el requisito de reinicio en caso de que el script se bloquee o se elimine por algún motivo.
jlliagre
@jlliagre, gracias por el comentario. En la respuesta, no veo una especificación clara para el comportamiento previsto después de un bloqueo o muerte. Esta es claramente una pregunta interesante y relevante. Por el momento lo mantendré simple (si el guión murió o fue asesinado, quédese muerto :)
JJoao
@jlliagre Para eso están las excepciones. Pero Perl podría no ser la opción más adecuada en este caso, ya que no admite el mecanismo de excepción. Solo una suposición. Sería mejor portar el script a un idioma que admita excepciones. Todo depende de cuán grande sea el trabajo de portabilidad, por supuesto.
@Nasha, en Perl, con manejadores de señal, variables de error, Tyr :: Tiny, etc., ¡podemos hacerlo! ... pero - Odio el manejo de excepciones y la recuperación de errores: nunca están completos ...
JJoao
1
@JJoao Estoy de acuerdo con usted en el hecho de que la recuperación de errores rara vez se completa. Por supuesto, corresponde al desarrollador cubrir todos los casos posibles. Así es con los PLC en el mundo de la industria, por lo tanto, debe ser bastante posible después de todo ;-).
3

Cuando su script perl está destinado a seguir ejecutándose todo el tiempo, ¿por qué usar la construcción while? Cuando el perl falla en vista de algún problema grave, la nueva secuencia de comandos perl iniciada por el tiempo podría bloquearse igual de duro. Una y otra vez y así sucesivamente.
si realmente quieres que tu perl comience de nuevo, considera crontab y un script que primero verifica las instancias en ejecución. De esta manera, su script incluso se iniciará después de un reinicio.

Walter A
fuente
2

En general, no hay ningún problema al usarlo, while trueya que es una pequeña prueba que solo se ejecuta después de que finaliza el script perl. Tenga en cuenta que, según la variante de Linux / Unix que esté utilizando, el script podría finalizar al cerrar la sesión. En tal caso, considere usar el bucle en un script y llámelo con nohupy póngalo en segundo plano, es decirnohup myscript &

Si el script perl termina con demasiada frecuencia y causa una carga de CPU, esta carga es responsable del script perl y no del while true.

Ver también man nohuppara más detalles.

Lambert
fuente
El único problema es la posibilidad de un bucle dinámico que impulsa la utilización de la CPU. Así que estoy totalmente de acuerdo con poner una llamada de suspensión dentro del ciclo para domarlo.
Craig
2

Si desea administrar un proceso, es posible que desee buscar un administrador de procesos para hacerlo.

Con muchos sistemas recientes, puede usar systemd para monitorear sus procesos (que es uno de los beneficios de systemd sobre los scripts de inicio clásicos). Si su distribución preferida no usa systemd, puede usar daemontools o monit .

Andrew Aylett
fuente
mones una alternativa simple, si la enorme humanidad de monit y systemd te molesta tanto como a mí.
Anko
2

El whilebucle no es realmente una buena idea. No hay escapatoria allí, solo funciona para siempre, estáticamente . Cualquier cantidad de cosas podría cambiar en el medio ambiente y no se verá afectada, y esto podría ser malo.

Por ejemplo, si whilese actualiza el ejecutable del shell responsable de ese ciclo, el kernel no podrá liberar el espacio en disco para la versión anterior hasta que se cierre el script porque necesita mantener el descriptor mientras se ejecuta. Y eso es cierto para cualquier archivo que el shell pueda haber abierto por cualquier razón para ejecutar el bucle; todos se mantendrán abiertos por este whilebucle durante el tiempo que se ejecute, para siempre.

Y si, Dios no lo quiera, hay una pérdida de memoria en cualquier parte del shell que ejecuta ese bucle, incluso el más mínimo, simplemente continuará perdiendo estáticamente . Se construirá sin control y el único recurso es matarlo a la fuerza, luego comenzar de nuevo solo para hacer lo mismo más tarde.

Esta no es la forma en que debería configurar un proceso en segundo plano, al menos, no en mi opinión. En cambio, como creo, debería haber un punto de reinicio: una actualización del script y su estado. La forma más fácil de hacer esto es con exec. Puede reemplazar el proceso actual por uno nuevo, mientras mantiene el mismo PID, pero aún ejecuta un nuevo proceso.

Por ejemplo, si su perlscript debe devolver verdadero después de que haya manejado con éxito una modificación de archivo en algún directorio monitoreado:

#!/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 algo así. Algo que permite que la maquinaria básica detrás del bucle se actualice de vez en cuando.

mikeserv
fuente
1

Voté algunas de estas respuestas, pero instintivamente tengo los sentimientos más cálidos por la respuesta de @ WalterA. Bueno, lo hice hasta que creé mi propia respuesta ...

Personalmente, tiendo a actualizar el script de Perl para que escriba una entrada de registro descriptiva sobre la falla y envíe una alerta a un administrador.

Si el script Perl está destinado a continuar ejecutándose, esperando eventos de cambio del sistema de archivos, ¿por qué falla?

Si no falla, ¿por qué le preocupa envolverlo en un script para reiniciarlo infinitamente?

Si hay un problema de configuración o alguna dependencia rota que hace que el script Perl se cancele, simplemente reiniciarlo una y otra vez no hará que de repente comience a funcionar.

Conoces la definición de locura, ¿verdad? (haciendo lo mismo una y otra vez, esperando un resultado diferente). Solo digo. ;-)

Craig
fuente
jaja- Lo sé, lo sé. pero ya sabes, el mundo real apesta. De todos modos, la razón es que procesa archivos, y es posible que algunos archivos ni siquiera se transmitan correctamente. Todavía no conocemos la variedad de razones por las que puede fallar. Veo su punto sobre registrar el error, pero aún necesitaré una copia de seguridad para procesar el siguiente archivo a través de algo como supervisor
Abhinav Gujjar
Si puede detectar las excepciones, puede registrarlas y luego seguir procesando, e incluso regresar y volver a intentar ocasionalmente los archivos fallidos? Tal vez el fichero no fue bloqueado por otro usuario o proceso cuando intentó procesarlo, etc.
Craig