Wie kann ein Unix-Skript alle 15 Sekunden ausgeführt werden?

84

Ich habe einige Lösungen gesehen, darunter das Beobachten und einfache Ausführen eines Schleifenskripts (und eines Schlafskripts) im Hintergrund, aber nichts war ideal.

Ich habe ein Skript, das alle 15 Sekunden ausgeführt werden muss, und da cron keine Sekunden unterstützt, muss ich etwas anderes herausfinden.

Was ist die robusteste und effizienteste Methode, um unter Unix alle 15 Sekunden ein Skript auszuführen? Das Skript muss auch nach einem Neustart ausgeführt werden.

Nick Sergeant
quelle
1
Wie lange dauert der Lauf?
Adam Matan

Antworten:

76

Ich würde cron verwenden, um jede Minute ein Skript auszuführen, und dieses Skript dazu bringen, Ihr Skript viermal mit einem Ruhezustand von 15 Sekunden zwischen den Läufen auszuführen.

(Dies setzt voraus, dass Ihr Skript schnell ausgeführt wird. Wenn nicht, können Sie die Ruhezeiten anpassen.)

Auf diese Weise erhalten Sie alle Vorteile cronsowie Ihre Laufzeit von 15 Sekunden.

Bearbeiten: Siehe auch den Kommentar von @ bmb unten.

RichieHindle
quelle
@Aiden: Ha! Meine Erzfeindin, wir treffen uns wieder!
RichieHindle
55
Wenn das Skript nicht konsistent ist, wie lange es dauert, erstellen Sie vier Kopien des Skripts. Man schläft 15 Sekunden vor dem Start, weitere 30, weitere 45 und eine weitere Null. Führen Sie dann alle vier jede Minute aus.
Bmb
@RichieHindle - Hab keine Angst, ich wurde ermordet, weil ich die Minuten nicht in Sekunden zerlegt habe. Aber ich beobachte dich: P
Aiden Bell
Wie kann das sein
?
Eigentlich sollte das Outter-Skript das innere Skript dreimal ausführen, nicht viermal. Andernfalls überlappt der letzte Lauf der letzten Minute den ersten Lauf der nächsten Minute. Das würde das innere Skript fünfmal pro Minute anstatt viermal ausführen.
Tulains Córdova
288

Wenn Sie darauf bestehen, Ihr Skript von cron aus auszuführen:

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

und ersetzen Sie Ihren Skriptnamen und den Pfad zu / foo / bar / your_script

Rasjani
quelle
18
das ist sehr klug. +1
SingleNegationElimination
4
Das hat bei mir perfekt funktioniert. Die obige Lösung zur Verwendung einer Hintergrundaufgabe führte dazu, dass mehrere untergeordnete Prozesse erzeugt wurden und Speicherprobleme bei mir auftraten.
Hacknightly
2
Wenn Sie ein PHP-Skript ausführen, gehen Sie wie folgt vor:* * * * * sleep 15; php /foo/bar/your_script
ImaginedDesign
1
Wenn #!/usr/bin/phpSie ein PHP-Skript ausführen, können Sie die Zeile an die Spitze Ihres PHP-Skripts stellen und es ausführbar machen
Aaron Blenkush
18
Es ist mir peinlich, dass ich für diese Lösung googeln musste. Vielleicht lässt mich Stackoverflow weniger nachdenken.
Chris Finne
15

Geänderte Version der oben genannten:

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

zu / etc / crontab hinzufügen:

* * * * * 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
quelle
13

Wird dies nicht im Hintergrund ausgeführt?

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

Dies ist ungefähr so ​​effizient wie es nur geht. Der wichtige Teil wird nur alle 15 Sekunden ausgeführt und das Skript schläft den Rest der Zeit (wodurch keine Zyklen verschwendet werden).

scvalex
quelle
8
Die Bearbeitungen müssen mindestens 8 Zeichen umfassen (was meiner Meinung nach idiotisch ist), daher konnte ich die &am Ende von Zeile 3 nicht hinzufügen . Auf jeden Fall wird dies nicht alle 15 Sekunden ausgeführt. Dies läuft alle "15 Sekunden + wie lange es echo hellodauert". Welches könnte 0,01 Sekunden sein; könnte 19 Stunden sein.
Parthian Shot
1

Ich habe einen Scheduler schneller als Cron geschrieben. Ich habe auch einen überlappenden Schutz implementiert. Sie können den Scheduler so konfigurieren, dass kein neuer Prozess gestartet wird, wenn der vorherige noch ausgeführt wird. Schauen Sie sich https://github.com/sioux1977/scheduler/wiki an

Siouxes
quelle
0

Verwenden Sie Nanosleep (2) . Es verwendet eine Struktur timespec, mit der Zeitintervalle mit Nanosekundengenauigkeit festgelegt werden.

struct timespec {
           time_t tv_sec;        /* seconds */
           long   tv_nsec;       /* nanoseconds */
       };
Alexander Suraphel
quelle
1
Ich werde weitermachen und vermuten, dass sie keine Nanosekundengenauigkeit benötigen, da das, was sie alle 15 Sekunden erzeugen, ein Shell-Skript ist, kein Kernel-Thread.
Parthian Shot
@ParthianShot vielleicht, aber du weißt es nie.
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
quelle
0

Seit meiner vorherigen Antwort habe ich eine andere Lösung gefunden, die anders und vielleicht besser ist. Mit diesem Code können Prozesse mehr als 60 Mal pro Minute mit einer Genauigkeit von Mikrosekunden ausgeführt werden. Sie benötigen das usleep-Programm, damit dies funktioniert. Sollte bis zu 50 Mal pro Sekunde gut sein.

#! /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
quelle
1
Bearbeiten Sie das Original, anstatt es erneut zu veröffentlichen!
Vogon Jeltz
-1

Verwenden Sie einen in diesem Thread beschriebenen Sperrmechanismus, um mögliche Überschneidungen bei der Ausführung zu vermeiden .

flm
quelle
2
-1 Das OP sagte nicht, dass er eine überlappende Ausführung vermeiden müsse; das Ding könnte wiedereintrittsfähig sein. Außerdem beantwortet dies die Frage nicht.
Parthian Shot