Comment faire exécuter un script Unix toutes les 15 secondes?

84

J'ai vu quelques solutions, y compris regarder et simplement exécuter un script en boucle (et en veille) en arrière-plan, mais rien n'a été idéal.

J'ai un script qui doit s'exécuter toutes les 15 secondes, et comme cron ne prend pas en charge les secondes, je suis obligé de trouver autre chose.

Quel est le moyen le plus robuste et le plus efficace d'exécuter un script toutes les 15 secondes sous Unix? Le script doit également s'exécuter après un redémarrage.

Nick Sergeant
la source
1
Combien de temps faut-il pour fonctionner?
Adam Matan

Réponses:

77

J'utiliserais cron pour exécuter un script toutes les minutes, et je le ferais exécuter votre script quatre fois avec un sommeil de 15 secondes entre les exécutions.

(Cela suppose que votre script est rapide à exécuter - vous pouvez ajuster les temps de sommeil sinon.)

De cette façon, vous bénéficiez de tous les avantages cronainsi que de votre période d'exécution de 15 secondes.

Edit: Voir aussi le commentaire de @ bmb ci-dessous.

RichieHindle
la source
@Aiden: Ha! Mon ennemi, on se retrouve!
RichieHindle
56
Si le script n'est pas cohérent en termes de temps d'exécution, faites quatre copies du script. On dort 15 secondes avant de commencer, 30 autres, 45 autres et un autre zéro. Ensuite, exécutez les quatre toutes les minutes.
bmb le
@RichieHindle - N'ayez crainte, je me suis fait assassiner pour ne pas avoir granulé les minutes en secondes. Mais je vous regarde: P
Aiden Bell
Comment cela peut-il être déclenché toutes les 1 minute
DevZer0
En fait, le script externe doit exécuter le script interne trois fois, et non quatre. Sinon, la dernière exécution de la dernière minute chevauchera la première exécution de la minute suivante. Ce serait exécuter le script interne 5 fois par minute au lieu de quatre.
Tulains Córdova
290

Si vous insistez pour exécuter votre script depuis cron:

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

et remplacez votre nom de script et le chemin vers / foo / bar / votre_script

Rasjani
la source
18
c'est très intelligent. +1
SingleNegationElimination
4
Cela a parfaitement fonctionné pour moi. La solution ci-dessus sur l'utilisation d'une tâche en arrière-plan engendrait plusieurs processus enfants et provoquait des problèmes de mémoire de mon côté.
Hacknightly
2
si vous exécutez un script php, procédez comme suit:* * * * * sleep 15; php /foo/bar/your_script
ImaginedDesign
1
si vous exécutez un script php, vous pouvez ajouter la ligne #!/usr/bin/phpen haut de votre script php et le rendre exécutable
Aaron Blenkush
18
Je me sens gêné d'avoir dû chercher cette solution sur Google. Peut-être que stackoverflow me fait moins réfléchir.
chris finne
15

Version modifiée de ce qui précède:

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

ajouter à / 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
la source
13

Est-ce que cela ne fonctionne pas en arrière-plan?

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

C'est à peu près aussi efficace que possible. La partie importante n'est exécutée que toutes les 15 secondes et le script s'endort le reste du temps (donc pas de cycles de perte).

scvalex
la source
8
Les modifications doivent comporter au moins 8 caractères (ce qui est idiot, à mon humble avis) donc je ne pourrais pas ajouter le &à la fin de la ligne 3. En tout cas, tel quel, cela ne s'exécute pas toutes les 15 secondes. Cela s'exécute toutes les "15 secondes + quel que soit le temps echo hellode fonctionnement". Ce qui pourrait être 0,01 seconde; pourrait durer 19 heures.
Tir parthe du
1

J'ai écrit un ordonnanceur plus rapidement que cron. J'ai également mis en place une garde de chevauchement. Vous pouvez configurer le planificateur pour ne pas démarrer de nouveau processus si le précédent est toujours en cours d'exécution. Jetez un œil à https://github.com/sioux1977/scheduler/wiki

sioux
la source
0

Utilisez nanosleep (2) . Il utilise une structure timespecqui est utilisée pour spécifier des intervalles de temps avec une précision de l'ordre de la nanoseconde.

struct timespec {
           time_t tv_sec;        /* seconds */
           long   tv_nsec;       /* nanoseconds */
       };
Alexandre Suraphel
la source
1
Je vais aller de l'avant et deviner qu'ils n'ont pas besoin d'une précision à la nanoseconde, car ce qu'ils génèrent toutes les 15 secondes est un script shell, pas un thread du noyau.
Tir parthe
@ParthianShot peut-être mais on ne sait jamais.
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
user3174711
la source
0

Depuis ma réponse précédente, j'ai trouvé une autre solution qui est différente et peut-être meilleure. Ce code permet aux processus d'être exécutés plus de 60 fois par minute avec une précision de l'ordre de la microseconde. Vous avez besoin du programme usleep pour que cela fonctionne. Devrait être bon jusqu'à 50 fois par seconde.

#! /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
user3174711
la source
1
Modifiez l'original au lieu de publier à nouveau!
Vogon Jeltz
-1

Pour éviter d'éventuels chevauchements d'exécution, utilisez un mécanisme de verrouillage comme décrit dans ce thread .

flm
la source
2
-1 Le PO n'a pas dit qu'il devait éviter les chevauchements d'exécution; la chose pourrait être réentrante. De plus, cela ne répond pas à la question.
Tir parthe du