Come fare in modo che uno script unix venga eseguito ogni 15 secondi?


84

Ho visto alcune soluzioni, tra cui guardare e semplicemente eseguire uno script in loop (e inattivo) in background, ma niente è stato l'ideale.

Ho uno script che deve essere eseguito ogni 15 secondi e poiché cron non supporterà i secondi, sono lasciato a capire qualcos'altro.

Qual è il modo più robusto ed efficiente per eseguire uno script ogni 15 secondi su unix? Lo script deve essere eseguito anche dopo un riavvio.


1
Quanto tempo ci vuole per correre?
Adam Matan

Risposte:


77

Userei cron per eseguire uno script ogni minuto e fare in modo che lo script esegua lo script quattro volte con una sospensione di 15 secondi tra le esecuzioni.

(Ciò presuppone che lo script sia veloce da eseguire, in caso contrario potresti regolare i tempi di sospensione.)

In questo modo, ottieni tutti i vantaggi crone il periodo di esecuzione di 15 secondi.

Modifica: vedi anche il commento di @ bmb di seguito.


@ Aiden: Ha! Mia nemesi, ci incontriamo di nuovo!
RichieHindle,

56
Se lo script non è coerente nel tempo impiegato per l'esecuzione, crea quattro copie dello script. Uno dorme 15 secondi prima di iniziare, altri 30, altri 45 e un altro zero. Quindi eseguili tutti e quattro ogni minuto.
bmb

@RichieHindle - Non temere, sono stato assassinato per non aver granulato i minuti in secondi. Ma ti sto guardando: P
Aiden Bell,

Come può essere questo cron viene attivato ogni minuto
DevZer0

In realtà lo script esterno dovrebbe eseguire lo script interno tre volte, non quattro. In caso contrario, l'ultima corsa dell'ultimo minuto si sovrapporrà alla prima corsa del minuto successivo. Sarebbe eseguire lo script interno 5 volte al minuto invece di quattro.
Tulains Córdova

292

Se insisti a eseguire lo script da cron:

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

e sostituisci il nome e il percorso dello script in / foo / bar / your_script


4
Ha funzionato perfettamente per me. La soluzione sopra questa sull'utilizzo di un'attività in background è stata la generazione di diversi processi figlio e la mia fine di problemi di memoria.
Hacknightly

3
se si esegue lo script php, eseguire questa operazione:* * * * * sleep 15; php /foo/bar/your_script
ImaginedDesign

1
se esegui uno script php puoi anteporre la riga #!/usr/bin/phpall'inizio dello script php e renderlo eseguibile
Aaron Blenkush

18
Mi sento in imbarazzo per aver dovuto cercare su Google questa soluzione. Forse stackoverflow mi fa pensare di meno.
chris finne

In risposta a @Hacknightly, accade solo quando gli script superano il tempo di esecuzione di 15 secondi e / o le attività non sono in grado di rilasciare memoria sufficiente che può essere utilizzata dal sistema
Abel Callejo

15

Versione modificata di quanto sopra:

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

aggiungi 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

13

Non lo eseguirai in background?

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

Questo è quanto più efficiente possibile. La parte importante viene eseguita solo ogni 15 secondi e lo script dorme per il resto del tempo (quindi senza sprecare cicli).


8
Le modifiche devono contenere almeno 8 caratteri (che è idiota, IMHO) quindi non ho potuto aggiungere &alla fine della riga 3. In ogni caso, così com'è, non viene eseguito ogni 15 secondi. Questo viene eseguito ogni "15 secondi + per quanto tempo echo helloimpiega per funzionare". Quale potrebbe essere 0,01 secondi; potrebbero essere 19 ore.
Parthian Shot

1

Ho scritto uno scheduler più veloce di cron. Ho anche implementato una guardia sovrapposta. È possibile configurare lo scheduler per non avviare un nuovo processo se quello precedente è ancora in esecuzione. Dai un'occhiata a https://github.com/sioux1977/scheduler/wiki


0

Usa nanosleep (2) . Utilizza la struttura timespecche viene utilizzata per specificare intervalli di tempo con precisione al nanosecondo.

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

1
Vado avanti e immagino che non abbiano bisogno di una precisione al nanosecondo, poiché la cosa che generano ogni 15 secondi è uno script di shell, non un thread del kernel.
Parthian Shot

@ParthianShot forse ma non si sa mai.
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

0

Dalla mia precedente risposta ho trovato un'altra soluzione che è diversa e forse migliore. Questo codice consente di eseguire processi più di 60 volte al minuto con una precisione di microsecondi. Hai bisogno del programma usleep per farlo funzionare. Dovrebbe essere buono fino a 50 volte al secondo.

#! /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
Modifica quello originale invece di postare di nuovo!
Vogon Jeltz

-1

Per evitare possibili sovrapposizioni di esecuzione, utilizzare un meccanismo di blocco come descritto in quel thread .


2
-1 L'OP non ha detto che doveva evitare l'esecuzione sovrapposta; la cosa potrebbe essere rientrante. Inoltre, questo non risponde alla domanda.
Parthian Shot
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.