¿Cómo hacer que un script de Unix se ejecute cada 15 segundos?

84

He visto algunas soluciones, que incluyen mirar y simplemente ejecutar un script en bucle (y dormir) en segundo plano, pero nada ha sido ideal.

Tengo una secuencia de comandos que debe ejecutarse cada 15 segundos y, dado que cron no admite segundos, me queda resolver otra cosa.

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

Sargento Nick
fuente
1
¿Cuánto tiempo tarda en ejecutarse?
Adam Matan

Respuestas:

77

Usaría cron para ejecutar un script cada minuto, y haría que ese script ejecute su script cuatro veces con un reposo de 15 segundos entre ejecuciones.

(Eso supone que su script se ejecuta rápidamente; de ​​lo contrario, podría ajustar los tiempos de suspensión).

De esa manera, obtendrá todos los beneficios crony también su período de ejecución de 15 segundos.

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

RichieHindle
fuente
@Aiden: ¡Ja! ¡Mi némesis, nos volvemos a encontrar!
RichieHindle
56
Si el script no es coherente en cuanto al tiempo que tarda en ejecutarse, haga cuatro copias del script. Uno duerme 15 segundos antes de empezar, otro 30, otro 45 y otro cero. Luego ejecute los cuatro cada minuto.
bmb
@RichieHindle - No temas, me asesinaron por no granular los minutos en segundos. Pero te estoy mirando: P
Aiden Bell
¿Cómo se puede activar este cron cada 1 minuto?
DevZer0
En realidad, el script externo debería ejecutar el script interno tres veces, no cuatro. De lo contrario, la última ejecución del último minuto se superpondrá a la primera ejecución del minuto siguiente. Eso sería ejecutar el script interno 5 veces por minuto en lugar de cuatro.
Tulains Córdova
290

Si insiste en ejecutar su script desde cron:

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

y reemplace el nombre y la ruta de su script a / foo / bar / your_script

rasjani
fuente
18
esto es muy inteligente. +1
SingleNegationElimination
4
Esto funcionó perfectamente para mí. La solución anterior sobre el uso de una tarea en segundo plano estaba generando varios procesos secundarios y causando problemas de memoria por mi parte.
Hacknightly
2
si ejecuta un script php, haga esto:* * * * * sleep 15; php /foo/bar/your_script
ImaginedDesign
1
si ejecuta un script php, puede anteponer la línea #!/usr/bin/phpa la parte superior de su script php y hacerlo ejecutable
Aaron Blenkush
18
Me siento avergonzado de tener que buscar en Google esta solución. Quizás stackoverflow me haga pensar menos.
chris finne
15

Versión modificada de lo anterior:

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

agregar 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
Marc Perkel
fuente
13

¿No funcionará ejecutar esto en segundo plano?

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

Esto es tan eficiente como parece. La parte importante solo se ejecuta cada 15 segundos y el script duerme el resto del tiempo (por lo tanto, no desperdicia ciclos).

scvalex
fuente
8
Las ediciones deben tener al menos 8 caracteres (lo cual es una idiotez, en mi humilde opinión), por lo que no pude agregar &al final de la línea 3. En cualquier caso, tal como está, esto no se ejecuta cada 15 segundos. Esto se ejecuta cada "15 segundos + sin echo helloimportar el tiempo que tarde en ejecutarse". Lo que podría ser de 0,01 segundos; podrían ser 19 horas.
Disparo parto
1

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

siouxes
fuente
0

Utilice nano sueño (2) . Utiliza una estructura timespecque se utiliza para especificar intervalos de tiempo con precisión de nanosegundos.

struct timespec {
           time_t tv_sec;        /* seconds */
           long   tv_nsec;       /* nanoseconds */
       };
Alexander Suraphel
fuente
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 del kernel.
Disparo parto
@ParthianShot quizás, pero nunca se sabe.
Alexander Suraphel
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
usuario3174711
fuente
0

Desde mi respuesta anterior, se me ocurrió otra solución que es diferente y quizás 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. Debería 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
usuario3174711
fuente
1
¡Edita el original en lugar de publicarlo de nuevo!
Vogon Jeltz
-1

Para evitar una posible superposición de ejecución, utilice un mecanismo de bloqueo como se describe en ese hilo .

pelicula
fuente
2
-1 El OP no dijo que necesitaba evitar la superposición de ejecuciones; la cosa podría volver a entrar. Además, esto no responde a la pregunta.
Disparo parto