Jak mogę utworzyć zadanie CRON, które uruchamia zadanie co trzy tygodnie?

9

Mam zadanie, które należy wykonać zgodnie z harmonogramem projektu (3 tygodnie).

Jestem w stanie skonfigurować crona tak, aby robił to co tydzień lub (na przykład) w trzecim tygodniu każdego miesiąca - ale nie mogę znaleźć sposobu na zrobienie tego co trzy tygodnie.

Mógłbym zhakować skrypt, aby utworzyć pliki tymczasowe (lub podobne), aby mógł się zorientować, że był uruchamiany po raz trzeci - ale to rozwiązanie pachnie.

Czy można to zrobić w czysty sposób?

itj
źródło
@itj 3 tygodnie = 21 dni, więc dlaczego nie umieścić zadania cron co 21 dni?
Studer
1
@studer Mogłem coś przeoczyć, ale nie sądziłem, że crontab jest tak elastyczny
itj

Odpowiedzi:

8

Plik crontab pozwala tylko określić:

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)

Nie można więc określić, które tygodnie powinny obowiązywać.

Najlepszym rozwiązaniem może być napisanie skryptu opakowania.
Możesz uzyskać numer tygodnia w skrypcie powłoki za pomocą

date +%U

lub

date +%V

w zależności od tego, czy chcesz rozpocząć tydzień w niedzielę czy poniedziałek.
Więc możesz użyć

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

A czek $ wyniósłby 0 w 1., 4., 7., ... tygodniu roku.

njd
źródło
3
Co stanie się pod koniec roku? Czy będzie biegał więcej razy, czy pominie tydzień czy coś, skoro rok nie dzieli się równo na wielokrotność 3 tygodni?
davr
Dziękuję za odpowiedź. Lepszy niż jakikolwiek pomysł, jaki miałem. Jest ważny tylko przez 1 rok - ale powinno być w porządku dla większości projektów.
itj
W ciągu wielu lat w Boże Narodzenie i 1 stycznia będziesz mieć dwa odstępy w odstępach 8 lub 9 dni, więc wtedy musisz to zrobić poprawnie i zapisać gdzieś „datę ostatniego uruchomienia”, abyś mógł obliczyć 3 tygodnie interwał.
njd
3

Dzięki wcześniejszym odpowiedziom wskazano dotychczasowe opcje epoki -e lub formatowanie jako% s

Chociaż trochę bolesne ((date +%s) / 86400)daje dni z epoki.

Polegając na cotygodniowej pracy wykonywanej w tym samym czasie, łatwo jest to sprawdzić na podstawie określonego dnia 3-tygodniowego okresu ( $epoch_day%21 == 13na przykład)

W moim przypadku jest to w porządku, ponieważ jest to jedno zadanie. Jeśli tego dnia przegapisz, nie musisz biegać przy następnej okazji.

itj
źródło
1

Jeśli możesz zapisać plik znacznika czasu między uruchomieniami, możesz sprawdzić jego datę zamiast polegać wyłącznie na bieżącej dacie.

Jeśli twoje polecenie find obsługuje wartości ułamkowe dla -mtime(lub ma -mmin) ( GNU find ma jedno i drugie , wydaje się , że POSIX również nie wymaga ), możesz „dusić” zadania cron za pomocą funkcji find i touch .

Lub, jeśli masz polecenie stat, które obsługuje wyświetlanie dat plików jako „sekund od epoki” (np. Stat z Gnu coreutils , a także innych implementacji), możesz wykonać własne porównanie, używając daty , statystyki i operatorów porównania powłoki (razem przy kontakcie zaktualizować plik znacznika czasu). Możesz także użyć ls zamiast stat, jeśli potrafi formatować (np. Ls z plików GNU ).

Poniżej znajduje się program w Perlu (nazwałem go n-hours-ago), który aktualizuje plik znacznika czasu i kończy się pomyślnie, jeśli oryginalny znacznik czasu był wystarczająco stary. Tekst użycia pokazuje, jak używać go we wpisie crontab, aby zdławić zadanie crona. Opisano również korekty dotyczące „oszczędności czasu dziennego” oraz sposób obsługi „późnych” znaczników czasu z poprzednich serii.

#!/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
źródło