Jak mogę rozpocząć cronjob 1 godzinę później każdego dnia?

16

Muszę zacząć cronjob codziennie, ale godzinę później każdego dnia. To, co mam do tej pory, działa w przeważającej części, z wyjątkiem 1 dnia roku:

0 0 * * * sleep $((3600 * (10#$(date +\%j) \% 24))) && /usr/local/bin/myprog

Gdy dzień roku będzie równy 365, zadanie rozpocznie się o 5:00, ale następnego dnia (nie licząc roku przestępnego) dzień będzie miał wartość 1, więc zadanie rozpocznie się o 1:00. Jak mogę pozbyć się tej narożnej skrzynki?

brzozowy
źródło
1
Czy jest jakiś powód, aby nie uruchamiać go co 25 godzin?
HalosGhost,
7
A jak dokładnie to zrobiłbyś? * / 25 w pozycji godziny nie rozwiąże go.
brzoza
@HalosGhost Dzięki za sugestię! Napisałem prostą implementację opartą na.
Giulio Muscarello,

Odpowiedzi:

23

Moim preferowanym rozwiązaniem byłoby uruchamianie zadania co godzinę, ale sam skrypt sprawdza, czy czas uruchomić, czy nie i kończy pracę bez robienia czegokolwiek 24 razy na 25.

crontab:

0 * * * *    /usr/local/bin/myprog

u góry myprog:

[ 0 -eq $(( $(date +%s) / 3600 % 25 )) ] || exit 0

Jeśli nie chcesz wprowadzać żadnych zmian w samym skrypcie, możesz również zaznaczyć pole „czas uruchomienia” we wpisie crontab, ale tworzy to długą, nieestetyczną linię:

0 * * * *    [ 0 -eq $(( $(date +\%s) / 3600 \% 25 )) ] && /usr/local/bin/myprog
Celada
źródło
1
'%' musi być poprzedzony ukośnikiem w crontab.
brzoza
@birch Nigdy tego nie wiedziałem! Chyba nigdy wcześniej nie próbowałem zawrzeć% w crontab. Dzięki za korektę, zredagowałem odpowiedź.
Celada,
1
To mi dobrze wygląda. Nie mam pojęcia, co PO chce zrobić w odniesieniu do czasu letniego (wiosna przed, opóźnienie), ale PO powinien odpowiednio dostosować.
emory
Byłbym trochę zaniepokojony zaokrągleniem w dywizji. Jeśli z jakiegoś powodu czas, który datewrócił z jądra, był 1mswcześniejszy niż oczekiwany czas uruchomienia skryptu, sprawdzenie dałoby niepoprawny wynik.
kasperd
1
@kasperd Nie wiem, przypuszczam, że masz rację co do różnych procesorów zgłaszających inny czas. Jeśli chodzi o ntpdto, stara się tylko zabić zegar, a nie przeskoczyć go, aby uniknąć tego rodzaju problemów, ale masz rację, to (lub ntpdate) czasami może przeskoczyć czas do tyłu. Jeśli chodzi o cronbłędne obliczenie opóźnienia snu, jestem pewien, że byłby to błąd! Mimo to, punkt rozważany, a obejściem byłoby zaplanowanie zadania 30 minut po godzinie, co jest mniej prawdopodobne, że spowoduje problem ... Lub dodaj ± 1800 w wyrażeniu arytmetycznym przed przyjęciem modemu 3600 reamainder.
Celada
7

Jeśli system ma systemd, możesz do tego użyć zdarzeń liczników czasu. Po prostu zdefiniuj nową usługę , która powinna zawierać polecenie / zadanie, które chcesz wykonać, a następnie utwórz zdarzenie timera z OnUnitActiveSecopcją:

[Unit]
Description=daily + 1 hour task

[Timer]
OnUnitActiveSec=25h # run 25 hours after service was last started
AccuracySec=10min

[Install]
WantedBy=timers.target

Użyj tej samej nazwy dla plików, tyle że zamiast tego .serviceużyj .timer.

Synteza:

  1. Utwórz plik o nazwie job.servicew /etc/systemd/system/katalogu.
  2. Wpisz niezbędne informacje. Możesz zweryfikować konfigurację, używając systemctl status job.service.
  3. Utwórz plik o nazwie job.timerin /etc/systemd/system/.
  4. Podaj wymagane informacje:

    [Unit]
    Description=daily + 1 hour task
    
    [Timer]
    OnUnitActiveSec=25h # run 25 hours after service was last started
    AccuracySec=10min
    
    [Install]
    WantedBy=timers.target
    
  5. Sprawdź czas za pomocą systemctl list-timers
  6. Gotowy.
Braiam
źródło
Gdybym chciał odejść od prostoty crona, użyłbym komendy launchd, która wymagałaby mniej pracy niż twój przykład dla systemd.
brzoza
6

Jeśli nie masz nic przeciwko użyciu czegoś innego niż cronjobs, sugerowałbym mniej znane narzędzie at. Po prostu napisz skrypt, który zaplanuje uruchomienie w ciągu 25 godzin, a następnie wywoła Twój program. To wydaje się być najczystszym rozwiązaniem.
Na przykład możesz napisać to w ~ / script.sh:

echo "bash ~/script.sh" | at now + 25 hours
/usr/bin/yourprogram

A potem po prostu bash ~/script.shraz uruchom .

Dzięki @HalosGhost za pomysł zaplanowania zadania raz na 25 godzin.

Giulio Muscarello
źródło
2
Istnieją 2 problemy z używaniem atdo tego celu: (1) jeśli zadanie nie wykona się poprawnie nawet raz, prawdopodobnie również nie przełoży się ponownie, a następnie nie tylko następne wykonanie, ale wszystkie przyszłe wykonania zostaną skutecznie anulowane, dopóki człowiek nie zauważy, i (2) ponieważ zadanie zajmuje trochę niezerowego czasu, użycie naiwnego now + 25 hoursoznacza, że ​​będzie ono działać za kilka sekund (lub dłużej) za każdym razem, a jeśli to opóźnienie narasta z czasem, co ostatecznie powoduje, że działa całkowicie źle czas.
Celada,
2
Masz rację co do # 1; Nie jestem pewien co do # 2. Chociaż nie mam żadnych danych, nie sądzę, aby opóźnienie między zmianą zegara a uruchomieniem zadania i jego późniejszym przesunięciem było wystarczająco duże, aby było zauważalne - szczególnie biorąc pod uwagę, że rozdzielczość atjest ograniczona do minut i rozpoczyna wszystkie zadania o [czas]: 00.
Giulio Muscarello,
1
@Celada: Planowanie następnej atpracy pierwszą rzeczą w skrypcie na uniknięcie tych problemów. Niezależnie od tego, jeśli wystąpi błąd, cały łańcuch jest zepsuty, co może, ale nie musi być pożądane: jeśli przypadek użycia to „uruchom to tylko wtedy, gdy działa”, to nie restartowanie jest świetną funkcją. Ale jeśli przypadek użycia jest „zawsze uruchamiany, nawet jeśli ostatni nie powiódł się”, atto nie jest właściwym narzędziem.
biskup