2009-06-23 30 views
77

He visto algunas soluciones, incluido el reloj y simplemente ejecutar un script de bucle (y dormir) en el fondo, pero nada ha sido ideal.¿Cómo obtener un script de Unix para ejecutarse cada 15 segundos?

Tengo un script que debe ejecutarse cada 15 segundos, y dado que cron no admitirá segundos, me queda la idea de encontrar algo más.

¿Cuál es la forma más robusta y eficiente de ejecutar un script cada 15 segundos en Unix? La secuencia de comandos también debe ejecutarse después de un reinicio.

+1

¿Cuánto tiempo lleva correr? –

Respuesta

73

Usaría cron para ejecutar un script cada minuto y haré que el script ejecute el script cuatro veces con un descanso de 15 segundos entre ejecuciones.

(Eso supone que su guión es rápida para correr - se podía ajustar los tiempos de sueño si no).

De esta manera, se obtiene todos los beneficios de cron, así como su segundo período de ejecución 15.

Editar: Consulte también el comentario de @ bmb a continuación.

+0

* sacude el puño * jeje –

+0

@Aiden: ¡Ja! Mi némesis, nos encontramos de nuevo! – RichieHindle

+50

Si la secuencia de comandos no es coherente en cuánto tiempo tarda en ejecutarse, realice cuatro copias de la secuencia de comandos. Uno duerme 15 segundos antes de comenzar, otros 30, otros 45 y otro cero. Luego ejecuta los cuatro a cada minuto. – bmb

13

No se ejecutará esto en segundo plano, ¿o sí?

#!/bin/sh 
while [ 1 ]; do 
    echo "Hell yeah!" & 
    sleep 15 
done 

Esto es casi tan eficiente como se pone. La parte importante solo se ejecuta cada 15 segundos y el script duerme el resto del tiempo (sin perder ciclos).

+7

Las ediciones deben tener al menos 8 caracteres (lo cual es idiota, en mi humilde opinión) así que no pude agregar 'y' al final de la línea 3. En cualquier caso, como es, esto no se ejecuta cada 15 segundos. Esto se ejecuta cada "15 segundos + sin embargo, el eco echo hola tarda en ejecutarse". Que podría ser 0.01 segundos; podría ser 19 horas. –

257

Si insiste de ejecutar el script de cron:

* * * * * /foo/bar/your_script 
* * * * * sleep 15; /foo/bar/your_script 
* * * * * sleep 30; /foo/bar/your_script 
* * * * * sleep 45; /foo/bar/your_script 

y reemplazar su nombre del script & camino a/foo/bar/tu_script

+16

esto es muy inteligente. +1 – SingleNegationElimination

+4

Esto funcionó perfectamente para mí. La solución anterior a esta sobre el uso de una tarea en segundo plano estaba generando varios procesos secundarios y causando problemas de memoria en mi extremo. – Hacknightly

+2

si ejecuta el script php, haga esto: '* * * * * sleep 15; php/foo/bar/your_script' – ImaginedDesign

0

Uso nanosleep(2). Utiliza la estructura timespec que se utiliza para especificar intervalos de tiempo con precisión de nanosegundos.

struct timespec { 
      time_t tv_sec;  /* seconds */ 
      long tv_nsec;  /* nanoseconds */ 
     }; 
+1

Voy a seguir adelante y supongo que no necesitan una precisión de nanosegundos, ya que lo que están generando cada 15 segundos es un script de shell, no un hilo de kernel. –

+0

@ParthianShot, pero nunca se sabe. –

-2

Para evitar la posible superposición de la ejecución, utilizan un mecanismo de bloqueo como se describe en que thread.

+2

-1 El OP no dijo que necesitaba evitar la superposición de la ejecución; la cosa podría ser reentrada. Además, esto no responde la pregunta. –

+0

Esto hubiera sido un buen comentario, votaría –

14

versión modificada de lo anterior:

mkdir /etc/cron.15sec 
mkdir /etc/cron.minute 
mkdir /etc/cron.5minute 

añadir a/etc/crontab:

* * * * * root run-parts /etc/cron.15sec > /dev/null 2> /dev/null 
* * * * * root sleep 15; run-parts /etc/cron.15sec > /dev/null 2> /dev/null 
* * * * * root sleep 30; run-parts /etc/cron.15sec > /dev/null 2> /dev/null 
* * * * * root sleep 45; run-parts /etc/cron.15sec > /dev/null 2> /dev/null 

* * * * * root run-parts /etc/cron.minute > /dev/null 2> /dev/null 
*/5 * * * * root run-parts /etc/cron.5minute > /dev/null 2> /dev/null 
0
#! /bin/sh 

# Run all programs in a directory in parallel 
# Usage: run-parallel directory delay 
# Copyright 2013 by Marc Perkel 
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron" 
# Free to use with attribution 

if [ $# -eq 0 ] 
then 
    echo 
    echo "run-parallel by Marc Perkel" 
    echo 
    echo "This program is used to run all programs in a directory in parallel" 
    echo "or to rerun them every X seconds for one minute." 
    echo "Think of this program as cron with seconds resolution." 
    echo 
    echo "Usage: run-parallel [directory] [delay]" 
    echo 
    echo "Examples:" 
    echo " run-parallel /etc/cron.20sec 20" 
    echo " run-parallel 20" 
    echo " # Runs all executable files in /etc/cron.20sec every 20 seconds or 3 times a minute." 
    echo 
    echo "If delay parameter is missing it runs everything once and exits." 
    echo "If only delay is passed then the directory /etc/cron.[delay]sec is assumed." 
    echo 
    echo 'if "cronsec" is passed then it runs all of these delays 2 3 4 5 6 10 12 15 20 30' 
    echo "resulting in 30 20 15 12 10 6 5 4 3 2 executions per minute." 
    echo 
    exit 
fi 

# If "cronsec" is passed as a parameter then run all the delays in parallel 

if [ $1 = cronsec ] 
then 
    $0 2 & 
    $0 3 & 
    $0 4 & 
    $0 5 & 
    $0 6 & 
    $0 10 & 
    $0 12 & 
    $0 15 & 
    $0 20 & 
    $0 30 & 
    exit 
fi 

# Set the directory to first prameter and delay to second parameter 

dir=$1 
delay=$2 

# If only parameter is 2,3,4,5,6,10,12,15,20,30 then automatically calculate 
# the standard directory name /etc/cron.[delay]sec 

if [[ "$1" =~ ^(2|3|4|5|6|10|12|15|20|30)$ ]] 
then 
    dir="/etc/cron.$1sec" 
    delay=$1 
fi 

# Exit if directory doesn't exist or has no files 

if [ ! "$(ls -A $dir/)" ] 
then 
    exit 
fi 

# Sleep if both $delay and $counter are set 

if [ ! -z $delay ] && [ ! -z $counter ] 
then 
    sleep $delay 
fi 

# Set counter to 0 if not set 

if [ -z $counter ] 
then 
    counter=0 
fi 

# Run all the programs in the directory in parallel 
# Use of timeout ensures that the processes are killed if they run too long 

for program in $dir/* ; do 
    if [ -x $program ] 
    then 
     if [ "0$delay" -gt 1 ] 
     then 
     timeout $delay $program &> /dev/null & 
     else 
     $program &> /dev/null & 
     fi 
    fi 
done 

# If delay not set then we're done 

if [ -z $delay ] 
then 
    exit 
fi 

# Add delay to counter 

counter=$(($counter + $delay)) 

# If minute is not up - call self recursively 

if [ $counter -lt 60 ] 
then 
    . $0 $dir $delay & 
fi 

# Otherwise we're done 
0

Desde mi respuesta anterior me ocurrió otra solución que es diferente y tal vez mejor . Este código permite que los procesos se ejecuten más de 60 veces por minuto con una precisión de microsegundos. Necesita el programa usleep para que esto funcione. Debe ser bueno hasta 50 veces por segundo.

#! /bin/sh 

# Microsecond Cron 
# Usage: cron-ms start 
# Copyright 2014 by Marc Perkel 
# docs at http://wiki.junkemailfilter.com/index.php/How_to_run_a_Linux_script_every_few_seconds_under_cron" 
# Free to use with attribution 

basedir=/etc/cron-ms 

if [ $# -eq 0 ] 
then 
    echo 
    echo "cron-ms by Marc Perkel" 
    echo 
    echo "This program is used to run all programs in a directory in parallel every X times per minute." 
    echo "Think of this program as cron with microseconds resolution." 
    echo 
    echo "Usage: cron-ms start" 
    echo 
    echo "The scheduling is done by creating directories with the number of" 
    echo "executions per minute as part of the directory name." 
    echo 
    echo "Examples:" 
    echo " /etc/cron-ms/7  # Executes everything in that directory 7 times a minute" 
    echo " /etc/cron-ms/30  # Executes everything in that directory 30 times a minute" 
    echo " /etc/cron-ms/600 # Executes everything in that directory 10 times a second" 
    echo " /etc/cron-ms/2400 # Executes everything in that directory 40 times a second" 
    echo 
    exit 
fi 

# If "start" is passed as a parameter then run all the loops in parallel 
# The number of the directory is the number of executions per minute 
# Since cron isn't accurate we need to start at top of next minute 

if [ $1 = start ] 
then 
    for dir in $basedir/* ; do 
     $0 ${dir##*/} 60000000 & 
    done 
    exit 
fi 

# Loops per minute and the next interval are passed on the command line with each loop 

loops=$1 
next_interval=$2 

# Sleeps until a specific part of a minute with microsecond resolution. 60000000 is full minute 

usleep $(($next_interval - 10#$(date +%S%N)/1000)) 

# Run all the programs in the directory in parallel 

for program in $basedir/$loops/* ; do 
    if [ -x $program ] 
    then 
     $program &> /dev/null & 
    fi 
done 

# Calculate next_interval 

next_interval=$(($next_interval % 60000000 + (60000000/$loops))) 

# If minute is not up - call self recursively 

if [ $next_interval -lt $((60000000/$loops * $loops)) ] 
then 
    . $0 $loops $next_interval & 
fi 

# Otherwise we're done 
+1

¡Edita el original en lugar de publicarlo de nuevo! –

1

Escribí un planificador más rápido que cron. También implementé una guardia superpuesta. Puede configurar el planificador para que no comience un nuevo proceso si el anterior aún se está ejecutando. Eche un vistazo a https://github.com/sioux1977/scheduler/wiki

Cuestiones relacionadas