Wie kann ich einen Cron-Job erstellen, der alle drei Wochen eine Aufgabe ausführt?

9

Ich habe eine Aufgabe, die in meinem Projektplan (3 Wochen) ausgeführt werden muss.

Ich kann cron einrichten , um dies jede Woche oder (zum Beispiel) in der 3. Woche eines jeden Monats zu tun - kann aber nicht alle drei Wochen einen Weg finden, dies zu tun.

Ich könnte das Skript hacken, um temporäre Dateien (oder ähnliches) zu erstellen, damit es funktioniert. Es war das dritte Mal, dass es ausgeführt wurde - aber diese Lösung riecht.

Kann es auf saubere Weise gemacht werden?

itj
quelle
@itj 3 Wochen = 21 Tage Warum also nicht alle 21 Tage eine Cron-Aufgabe stellen?
Studer
1
@studer ich etwas verpasst haben, aber ich glaube nicht , war crontab dass flexibel
itj

Antworten:

8

In der Crontab-Datei können Sie nur Folgendes angeben:

minute (0-59)
hour (0-23)
day of the month (1-31)
month of the year (1-12)
day of the week (0-6 with 0=Sunday)

Es ist also nicht möglich anzugeben, welche Wochen gelten sollen.

Das Schreiben eines Wrapper-Skripts ist möglicherweise die beste Option.
Sie können die Wochennummer in einem Shell-Skript erhalten, indem Sie verwenden

date +%U

oder

date +%V

je nachdem, ob Sie möchten, dass Ihre Wochen am Sonntag oder Montag beginnen.
Also könntest du verwenden

week=$(date +%V)
check=$(( ($week - 1) % 3 ))

Und $ check wäre in den Wochen 1, 4, 7, ... des Jahres 0.

njd
quelle
3
Was passiert am Ende des Jahres? Wird es zusätzliche Zeiten dauern oder eine Woche oder so etwas überspringen, da sich ein Jahr nicht gleichmäßig in ein Vielfaches von 3 Wochen aufteilt?
Davr
Danke für die Antwort. Ordentlicher als jede Idee, die ich hatte. Es ist nur 1 Jahr gültig - aber das sollte für die meisten Projekte in Ordnung sein.
Itj
Über mehrere Jahre hinweg würden Sie zwei Läufe im Abstand von 8 oder 9 Tagen um Weihnachten und den 1. Januar erhalten. Dann müssten Sie es richtig machen und das "Datum des letzten Laufs" irgendwo aufzeichnen, damit Sie eine 3-wöchige berechnen können Intervall.
NJD
3

Dank früherer Antworten wurde auf die Epochenoptionen bis Datum -e oder Formatierung als% s hingewiesen

Obwohl ein bisschen schmerzhaft ((date +%s) / 86400)gibt Tage aus der Epoche.

Wenn Sie sich darauf verlassen, dass der wöchentliche Job gleichzeitig ausgeführt wird, können Sie dies leicht anhand eines bestimmten Tages in einem Zeitraum von drei Wochen ( $epoch_day%21 == 13z. B.) überprüfen.

In meinem Fall ist dies in Ordnung, da es sich um eine One-Shot-Aufgabe handelt. Wenn es an einem bestimmten Tag verpasst wird, besteht keine Notwendigkeit, bei der nächsten Gelegenheit zu laufen.

itj
quelle
1

Wenn Sie eine Zeitstempeldatei zwischen den Läufen speichern können, können Sie das Datum überprüfen, anstatt sich ausschließlich auf das aktuelle Datum zu verlassen.

Wenn Ihr Befehl find Bruchwerte für -mtime(oder hat -mmin) unterstützt ( GNU find hat beides , POSIX scheint dies auch nicht zu erfordern ), können Sie die Cron-Jobs mit find and touch "drosseln" .

Wenn Sie einen stat- Befehl haben, der das Anzeigen von Dateidaten als "Sekunden seit der Epoche" unterstützt (z. B. stat von Gnu coreutils , auch andere Implementierungen), können Sie Ihren eigenen Vergleich mit date , stat und den Vergleichsoperatoren der Shell (zusammen) durchführen mit Berührung , um eine Zeitstempeldatei zu aktualisieren). Möglicherweise können Sie auch ls anstelle von stat verwenden, wenn die Formatierung möglich ist (z. B. ls aus GNU-Dateiutils ).

Unten finden Sie ein Perl-Programm (ich habe es genannt n-hours-ago), das eine Zeitstempeldatei aktualisiert und erfolgreich beendet wird, wenn der ursprüngliche Zeitstempel alt genug war. Der Verwendungstext zeigt, wie er in einem Crontab-Eintrag verwendet wird, um einen Cron-Job zu drosseln. Außerdem werden Anpassungen für die „Sommerzeit“ und den Umgang mit „späten“ Zeitstempeln aus früheren Läufen beschrieben.

#!/usr/bin/perl
use warnings;
use strict;
sub usage {
    printf STDERR <<EOU, $0;
usage: %s <hours> <file>

    If entry at pathname <file> was modified at least <hours> hours
    ago, update its modification time and exit with an exit code of
    0. Otherwise exit with a non-zero exit code.

    This command can be used to throttle crontab entries to periods
    that are not directly supported by cron.

        34 2 * * * /path/to/n-hours-ago 502.9 /path/to/timestamp && command

    If the period between checks is more than one "day", you might
    want to decrease your <hours> by 1 to account for short "days"
    due "daylight savings". As long as you only attempt to run it at
    most once an hour the adjustment will not affect your schedule.

    If there is a chance that the last successful run might have
    been launched later "than usual" (maybe due to high system
    load), you might want to decrease your <hours> a bit more.
    Subtract 0.1 to account for up to 6m delay. Subtract 0.02 to
    account for up to 1m12s delay. If you want "every other day" you
    might use <hours> of 47.9 or 47.98 instead of 48.

    You will want to combine the two reductions to accomodate the
    situation where the previous successful run was delayed a bit,
    it occured before a "jump forward" event, and the current date
    is after the "jump forward" event.

EOU
}

if (@ARGV != 2) { usage; die "incorrect number of arguments" }
my $hours = shift;
my $file = shift;

if (-e $file) {
    exit 1 if ((-M $file) * 24 < $hours);
} else {
    open my $fh, '>', $file or die "unable to create $file";
    close $fh;
}
utime undef, undef, $file or die "unable to update timestamp of $file";
exit 0;
Chris Johnsen
quelle