Hoe instrueer je cron om elke tweede week een taak uit te voeren?

Ik wil graag een taak door cron laten lopen die elke tweede dinsdag op een bepaald tijdstip van de dag wordt uitgevoerd. Want elke dinsdag is makkelijk:

0 6 * * Tue

Maar hoe maak je het op “elke tweede dinsdag” (of als je wilt – om de twee weken)?
Ik zou geen logica in het script zelf willen implementeren, maar de definitie alleen in cron houden.


Antwoord 1, autoriteit 100%

Antwoord

Wijzig uw dinsdag-cron-logica zodat deze om de week sinds het tijdperkwordt uitgevoerd.

Weten dat er 604800 seconden in een week zitten (de zomertijdveranderingen en schrikkelseconden negeren, dank u), en de GNU-datum gebruiken:

0 6 * * Tue expr `date +\%s` / 604800 \% 2 >/dev/null || /scripts/fortnightly.sh

Terzijde

Kalenderberekening is frustrerend.

@xahtep’s antwoord is geweldig, maar, zoals @Doppelganger opmerkte in opmerkingen, zal het op bepaalde jaargrenzen mislukken. Geen van de date-hulpprogramma’s jaar” bestekschrijvers kunnen hier helpen. Een dinsdag begin januari zal onvermijdelijk de weekpariteit van de laatste dinsdag van het voorgaande jaar herhalen: 2016-01-05 (%V), 2018-01-02 (%U) en 2019-01-01 (%W) .


Antwoord 2, autoriteit 94%

Wat dacht je hiervan, het houdt het wel in de crontab, zelfs als het niet precies is gedefinieerd in de eerste vijf velden:

0 6 * * Tue expr `date +\%W` \% 2 > /dev/null || /scripts/fortnightly.sh

Antwoord 3, autoriteit 15%

pilcrow’s antwoordis geweldig. Het resulteert er echter in dat het veertiendaagse.sh-script elke evenweek wordt uitgevoerd (sinds het tijdperk). Als je het script nodig hebt om onevenweken te draaien, kun je zijn antwoord een beetje aanpassen:

0 6 * * Tue expr \( `date +\%s` / 604800 + 1 \) \% 2 > /dev/null || /scripts/fortnightly.sh

Als u de 1 verandert in een 0, wordt deze teruggezet naar even weken.


Antwoord 4, autoriteit 10%

Misschien een beetje dom, maar je kunt ook twee cronjobs maken, één voor elke eerste dinsdag en één voor elke derde.

Eerste cronjob:

0 0 8 ? 1/1 TUE#1 *

Tweede cronjob:

0 0 8 ? 1/1 TUE#3 *

Niet zeker over de syntaxis hier, ik heb http://www.cronmaker.com/gebruikt om deze.


Antwoord 5, autoriteit 8%

Zoiets als

0 0 1-7,15-21 * 2

Zou de eerste en derde dinsdag van de maand zijn.

Opmerking: gebruik dit niet met vixie cron (inbegrepen in RedHat- en SLES-distributies), omdat het een of tussen de velden voor dag van de maand en dag van de week maakt in plaats van een en.


Antwoord 6, autoriteit 4%

Als je het wilt doen op basis van een bepaalde startdatum:

0 6 * * 1 expr \( `date +\%s` / 86400 - `date --date='2018-03-19' +\%s` / 86400 \) \% 14 == 0 > /dev/null && /scripts/fortnightly.sh

Moet vanaf 19-03-2018 om de andere maandag vuren

Uitdrukking luidt: Rijd op maandag om 6 uur als …

1 – Ontvang de datum van vandaag, in seconden, gedeeld door het aantal seconden in een dag om te converteren naar dagen in een tijdperk

2 – Doe hetzelfde voor de startdatum en converteer deze naar het aantal dagen sinds het tijdperk

3 – Ontdek het verschil tussen de twee

4 – deel door 14 en controleer de rest

5- Als de rest nul is, volgt u de cyclus van twee weken


Antwoord 7, autoriteit 2%

Ik ontdekte enkele aanvullende beperkingen van bovenstaande benaderingen die in sommige randgevallen kunnen mislukken. Denk bijvoorbeeld aan:

@xahtep en @Doppelganger bespraken problemen met het gebruik van %Wop bepaalde jaargrenzen hierboven.

@pilcrow’s antwoord lost dit tot op zekere hoogte op, maar het zal ook op bepaalde grenzen mislukken. Antwoorden in dit en of andere gerelateerde onderwerpen gebruiken het aantal seconden in een dag (vs. week), die om dezelfde redenen ook mislukken op bepaalde grenzen.

Dit komt omdat deze benaderingen afhankelijk zijn van UTC-tijd (datum +%s). Denk aan een geval waarin we elke 2e dinsdag om 01:00 en 22:00 uur een klus hebben.

Stel dat GMT-2:

  • 01:00 lokale tijd = 23:00 UTC gisteren
  • 22:00 uur lokale tijd = 20:00 uur UTC vandaag

Als we slechts één keer per dag controleren, is dit geen probleem, maar als we meerdere keren controleren — of als we bijna UTC-tijd hebben en zomertijd optreedt, houdt het script hier geen rekening mee dezelfde dag zijn.

Om dit te omzeilen, moeten we een afwijking van UTC berekenen op basis van onze lokaletijdzone, niet UTC. Ik kon geen eenvoudige manier vinden om dit in BASH te doen, dus ontwikkelde ik een oplossing die een snelle one-liner in Perl gebruikt om de offset van UTC in seconden te berekenen.

Dit script maakt gebruik van datum +%z, die de lokale tijdzone uitvoert.

Bash-script:

TZ_OFFSET=$( date +%z | perl -ne '$_ =~ /([+-])(\d{2})(\d{2})/; print eval($1."60**2") * ($2 + $3/60);' )
DAY_PARITY=$(( ( `date +%s` + ${TZ_OFFSET} ) / 86400 % 2 ))

vervolgens, om te bepalen of de dag even of oneven is:

if [ ${DAY_PARITY} -eq 1 ]; then
...
else
...
fi

Antwoord 8

Probeer dit

0 0 1-7,15-21 * 2

Dit wordt 2 keer per maand uitgevoerd, zowel op dinsdag als met een tussenpoos van minimaal een week.


Antwoord 9

Er zijn hier veel goede antwoorden. Op basis van de opmerkingen zie ik nogal wat verwarring en frustratie. Mijn bedoeling met dit antwoord is om niet alleen de OPs-vraag Hoe instrueer je cron om elke tweede week een taak uit te voeren?, maar verhelp ook wat verwarring voor mensen die dit in de toekomst kunnen lezen.

TL;DR:
Het crontab-item ziet er als volgt uit:

\<minute\> \<hour\> * * \<Day of Week\> expr \\( $( date+\\%s ) \\/ 604800 \\% 2 \\) > /dev/null && \<command to run on odd weeks\> || \<command to run on even weeks\>

Crontab-vermeldingen:
Alle crontab-items [gemaakt met crontab -e] hebben dit formaat, volgens de man-pagina:
1: De minuut van het uur om [opdracht] uit te voeren
Bereik: 0-59
2: Het uur van de dag om [opdracht] uit te voeren
Bereik: 0-23
3: De dag van de maand om [opdracht] uit te voeren
Bereik: 1-31
4: De maand om [opdracht] uit te voeren
Bereik: 1-12
5: De dag van de week om [opdracht] uit te voeren
Bereik: controleer je man-pagina, dit kan 0-6, 1-7 of 0-7 zijn
6: opdracht om uit te voeren
Let op, er zijn ook @-waarden, die ik hier niet zal bespreken.

Sommige versies van *nix staan ​​expressies toe:
*/2= elk even getal in het bereik
1 + */2= elk oneven getal binnen bereik
1,15= Eerste & 15e
2-8= Tweede tot en met de Achtste (inclusief)

Sommige versies laten ook de voor mensen leesbare woorden toe, zoals februari of woensdag.

Voor de maand wordt */2 uitgevoerd op feb, apr, jun, aug, okt, dec.

Voor dagen van de week wordt */2 elke even dag uitgevoerd — kijk op je man-pagina om te zien hoe de dagen van de week zijn genummerd. Di/2 is GEEN geldige uitdrukking.

Menselijk leesbare (kalender) ellende
Enkele constanten die u moet kennen:
Een jaar heeft 365,2464 dagen(voor het gemak ronden we af op 365).
Er zijn 12 maandenin een jaar.
Er zijn 7 dagenin een week.
Er zitten 86.400 secondenin een dag.

Daarom,
1 maand is 4-1/3 weken
[d.w.z. 3 maanden is 13 weken]
1 jaar is 52-1/7 weken

De vloek van kalenderwiskunde:
Halfmaandelijks = elke halve maand = 2x/maand = 24 keer per jaar.
Tweewekelijks = om de week (tweewekelijks) = 26 keer per jaar.
Opmerking: deze termen worden vaak verkeerd gebruikt.
Sommige jaren hebben 51 weken, andere 53, de meeste hebben 52. Als Mijn cron elke oneven week wordt uitgevoerd ( date +%Wmod 2), en het jaar heeft 51 of 53 weken, dan zal het ook lopen de volgende week, dat is week 1 van het nieuwe jaar. Omgekeerd, als mijn cron elke even week draait, zal het 2 weken overslaan. Niet wat ik wil.

CRON kan halfmaandelijks ondersteunen, niet tweewekelijks!
Halfmaandelijks:
De eerste van de maand valt altijd tussen de 1e & 7e. De tweede helft van de week valt altijd tussen de 15e en de 21e.
Halfmaandelijks zou 2 waarden hebben, één in de eerste helft en de andere in de tweede helft van de maand. Zoals:
2,16

Unix-tijd
Op een zeer hoog niveau is *nix time 2 waarden:
date +%s= Aantal seconden sinds Epoch (01/01/1970 00:00:00)
date +%N= fractionele seconden (in nanoseconden)
Daarom is de tijd in *nix date +%s.%N

*Nix gebruikt epochetijd. Het bestand /etc/shadow bevat de datum van de laatste wachtwoordwijziging. Het is het gehele deel van %s gedeeld door 86.400.

Factino
De GPS-satellieten meten de tijd als “Aantal weken sinds tijdperk” en “(fractionele) seconden in de week.
Opmerking: Epoch-weken zijn onafhankelijk van jaren. Het maakt niet uit of het jaar 51, 52 of 53 weken heeft. Epochweken gaan nooit voorbij.

Tweewekelijks tijdalgoritme
In *Nix is ​​datum +%W het weeknummer van het jaar, niet de epochweek. *nix heeft geen epochweekwaarde, maar kan wel worden berekend.
Epoch Week = Integer (Epoch Seconds / Seconds_per_Day / Days_per_Week )
Tweewekelijks = Epoch_Week Modulo 2
De waarde voor tweewekelijks zal altijd 0 of 1 zijn, dus het commando wordt uitgevoerd wanneer tweewekelijks = 0 elke even week wordt uitgevoerd, en 1 = elke oneven week.

Tweewekelijks computergebruik)
De eerste manier (bc):
date +"%s / 604800 % 2" | bc
Dit genereert “[Epoch Seconds] / 604800 % 2”
Dat antwoord wordt vervolgens naar bc gestuurd, een eenvoudige rekenmachine, die de wiskunde doet en het antwoord naar STDOUT en eventuele fouten naar STDERR herhaalt.
De retourcode is 0.

De tweede manier (expr):
Stuur in dit geval de uitdrukking naar expr en laat het de wiskunde doen
expr $( date +%s ) / 604800 % 2
Het expr-commando doet de wiskunde, echoot het antwoord naar STDOUT, fouten naar STDERR.
De retourcode is 1 als het antwoord 0 is, anders is de retourcode 0.
Op deze manier maakt het mij niet uit wat het antwoord is, alleen de retourcode. Overweeg dit:

$ expr 1 % 2 && echo Odd || echo Even
1
Odd 
$ expr 2 % 2 && echo Odd || echo Even 
0 
Even

Aangezien het resultaat er niet toe doet, kan ik STDOUT omleiden naar /dev/null. In dit geval verlaat ik STDERR voor het geval er iets onverwachts gebeurt, ik zie de foutmelding.

Crontab-invoer
In crontab hebben het procentteken en de haakjes een speciale betekenis, dus er moet een backslash (\) aan worden toegevoegd.
Eerst moet ik beslissen of ik op Even weken of oneven weken wil hardlopen. Of, misschien, voer Command_O uit op oneven weken en command_E op even weken. Dan moet ik alle speciale tekens escapen.
Aangezien de expr alleen WEKEN evalueert, is het noodzakelijk om een ​​bepaalde dag van de week (en tijd) op te geven om te evalueren, zoals elke dinsdag om 15:15 uur. Dus de eerste 5 bestanden (de tijdinvoer) van mijn crontab-invoer zullen zijn:
15 3 * * TUE
Het eerste deel van mijn cron-commando is het expr-commando, waarbij STDOUT naar null wordt gestuurd:

expr \\( $( date+\\%s ) \\/ 604800 \\% 2 \\) > /dev/null

Het tweede deel is de evaluator, &&of ||
Het derde deel is het commando (of script) dat ik wil uitvoeren. Dat ziet er zo uit:

expr \\( $( date+\\%s ) \\/ 604800 \\% 2 \\) > /dev/null && \<command_O\> || \<command_E\>

Hopelijk zal dat een deel van de verwarring ophelderen


Antwoord 10

Als je alleen elke dinsdag van de tweede week wilt:

00 06 * * 2#2


Antwoord 11

Syntaxis “/2” past niet bij de weekdag. Dus mijn toevoeging aan de slimme hierboven is gewoon 1,15 gebruiken op MonthDay ingediend.

0 6 1,15 * * /scripts/fornightly.sh > /dev/null 2>&1


Antwoord 12

0 0 */14 * *

Rijgt om 00:00 uur op elke 14e dag van de maand

Other episodes