Jak mogę powiedzieć cronowi, aby uruchamiał polecenie co drugi dzień (nieparzyste / parzyste)

45

Podczas konfigurowania crona do uruchamiania polecenia co drugi dzień przy użyciu pola „Dzień miesiąca”, tak jak poniżej:

1 22 */2 * * COMMAND

działa za każdym razem, gdy dzień miesiąca jest nieparzysty: 1,3,5,7,9 i tak dalej.

Jak skonfigurować crona tak, aby działał w dniach miesiąca podobnych do 2,6,8,10 itd. (Bez podania go dosłownie, co jest problematyczne, ponieważ każdy miesiąc ma inną liczbę dni w miesiącu)?

Freddie
źródło

Odpowiedzi:

60

Wypróbowana składnia jest w rzeczywistości niejednoznaczna. W zależności od liczby dni w miesiącu, niektóre miesiące będą działać w dni nieparzyste, a niektóre w parzyste. Wynika to z tego, że sposób jego obliczenia bierze całkowitą liczbę możliwości i dzieli je. Możesz prześcignąć to zachowanie polegające na stosowaniu strategii, ręcznie określając zakres dni i używając nieparzystej lub parzystej liczby dni. Ponieważ skrypty parzyste nigdy nie uruchomią się w 31 dniu dłuższych miesięcy, nie tracisz niczego, używając 30 dni jako podstawy dla dni parzystych, a określając konkretnie podział, tak jakby były 31 dni, możesz wymusić nieparzyste -dniowa egzekucja.

Składnia wyglądałaby następująco:

# Will only run on odd days:
0 0 1-31/2 * * command

# Will only run on even days:
0 0 2-30/2 * * command

Twoja obawa, że ​​miesiące nie będą miały takiej samej liczby dni, nie jest tutaj ważna, ponieważ żaden miesiąc nie ma WIĘCEJ dni niż ten, a dla biednego lutego zakres dat po prostu nigdy nie będzie pasował do ostatniego dnia lub dwóch, ale nie zaszkodzi to, że to wymienione.

Jedyną „gotcha” dla tego podejścia jest to, że jeśli jesteś w cyklu dnia nieparzystego, w następnych miesiącach z 31 dniami twoje polecenie będzie również działać pierwszego dnia miesiąca. Podobnie, jeśli wymuszasz cykl parzysty, każdy rok przestępny spowoduje jeden trzydniowy cykl i koniec lutego. Naprawdę nie można ominąć faktu, że jakikolwiek regularny wzorzec „co drugi dzień” nie zawsze będzie padał w parzyste lub nieparzyste dni w każdym miesiącu i w jakikolwiek sposób wymusisz to albo będziesz miał dodatkowy bieg, albo będzie go brakować między liczą się miesiące z niedopasowanym dniem.

Caleb
źródło
1
Dzięki, ale co się stanie w miesiącach takich jak luty, w których masz tylko 28 dni? Gwiazda faktycznie się tym zajmuje - ale rzeczywiście jest niejednoznaczna.
freddie
3
@freddie: Zobacz moją zredagowaną odpowiedź ... ale to nie jest problem, ponieważ wartości poza zakresem zostaną po prostu zignorowane, nic się nie stanie 30 lub 31 lutego. Zawsze. Możesz ręcznie określić za pomocą listy podobnej 0,2,4...,30,32,34i to nie będzie miało znaczenia, wartości spoza zakresu po prostu nigdy nie zostaną dopasowane.
Caleb
1
Dzięki! Rozumiem, dziękuję za pouczającą odpowiedź!
freddie
3
Na serwerze Ubuntu 8.04 wydaje się, że składnia wykorzystująca zero w dniu miesiąca jest nieprawidłowa (zły dzień w miesiącu). Akceptowana jest jednak następująca składnia:0 0 2-30/2 * * command
1
Fedora i RHEL 5,6,7 również nie lubią 0 jako dnia miesiąca. Jak wskazał użytkownik31053: 0 0 2-30/2 * * commanddziała zgodnie z oczekiwaniami.
NoelProf
2

Myślę, że istnieje możliwość wykorzystania dnia roku, takiego jak ten:

# for odd days
test $(((`date +%j` % 2))) != 0 && command

# for even days
test $(((`date +%j` % 2))) == 0 && command

Jest testowany dla systemów Unix i Linux.

Jordi
źródło
Wolę tę odpowiedź, ponieważ pomija problem z nieparzystą liczbą dni w niektórych miesiącach. W każdym razie sugeruję notację dolara zamiast test $(($(date +%j) % 2)) == 0 && command
odwrotnej
Sprawdziłem, że% j nie jest datą juliańską, więc najlepszy kod unikający przejścia na Nowy Rok należy obliczyć za pomocą sekund:test $(($(date +%s) / 86400 % 2)) == 0 && command
caligari
Dzięki za komentarze! Nasza propozycja działała dla nas jak urok. Użyliśmy tego schematu do codziennego uruchamiania cronów na różnych węzłach serwera, wszystkie one współużytkują ten sam crontab, ale skrypt pozwala na uruchomienie skryptu tylko na jednym z nich. Jeśli jednak będziemy musieli sprecyzować, rozważymy twoją propozycję. Dzięki!!!
Jordi,
1

Sprawdzajmy codziennie, czy jest to „inny” :-) ( bcwymagany program)

0 0 * * * test $(echo `date +%s` / 86400 % 2 == 0 |bc) -eq 0 && command

(Nie jestem pewien, czy kod pojawia się poprawnie. date +%sCzęść jest między apostrofami wstecznymi).

użytkownik37264
źródło
To będzie działać co drugi dzień, ale nie odpowiada na pytanie! Nadal będzie działać czasami w nieparzyste dni, a czasem nawet w dni parzyste, w zależności od miesiąca. Ma to taki sam wynik jak kod w pytaniu, po prostu osiągasz wynik, wykonując własną matematykę w ciągu kilku sekund od epoki. Działa to w parzyste dni od epoki, ale nie w parzyste dni naszego kalendarza.
Caleb
1

Ogólnie rzecz biorąc, uruchamiałbym go codziennie, a skrypt powinien użyć logiki, aby ustalić, czy należy go uruchomić dzisiaj.

Utworzenie prostego pliku statusu z informacją o ostatnim uruchomieniu, a następnie kompilacji, działałoby bardzo łatwo.

Jeśli trzeba go uruchamiać z różnych źródeł, uzależnij go od argumentów.

Klas Mattsson
źródło