Come ottenere uno script unix da eseguire ogni 15 secondi?
Domanda
Ho visto alcune soluzioni, tra cui guardare e semplicemente eseguire uno script in loop (e dormire) in background, ma nulla è stato l'ideale.
Ho uno script che deve essere eseguito ogni 15 secondi e poiché cron non supporterà 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.
Soluzione
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 il tuo script sia veloce da eseguire - in caso contrario potresti regolare i tempi di sospensione.)
In questo modo, ottieni tutti i vantaggi di cron
e il tuo periodo di esecuzione di 15 secondi.
Modifica: Vedi anche il commento di @ bmb di seguito.
Altri suggerimenti
Se insisti nell'eseguire il tuo 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 del tuo script e il percorso in / foo / bar / your_script
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
Non lo farai in background, vero?
#!/bin/sh
while [ 1 ]; do
echo "Hell yeah!" &
sleep 15
done
Questo è più efficiente che mai. La parte importante viene eseguita solo ogni 15 secondi e lo script rimane in pausa per il resto del tempo (quindi non sprecando cicli).
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 il precedente è ancora in esecuzione. Dai un'occhiata a https://github.com/sioux1977/scheduler/wiki
Usa nanosleep (2) . Utilizza la struttura timespec
che viene utilizzata per specificare intervalli di tempo con precisione in nanosecondi.
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
#! /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
<*> 2 &
<*> 3 &
<*> 4 &
<*> 5 &
<*> 6 &
<*> 10 &
<*> 12 &
<*> 15 &
<*> 20 &
<*> 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
. <*> $dir $delay &
fi
# Otherwise we're done
Dalla mia precedente risposta ho trovato un'altra soluzione diversa e forse migliore. Questo codice consente di eseguire i processi più di 60 volte al minuto con precisione al microsecondo. È necessario il 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
<*> ${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
. <*> $loops $next_interval &
fi
# Otherwise we're done
Per evitare possibili sovrapposizioni di esecuzione, utilizzare un meccanismo di blocco come descritto in filo .