web-dev-qa-db-fra.com

Comment demander à cron d'exécuter un travail toutes les deux semaines?

Je voudrais exécuter un travail avec cron qui sera exécuté un mardi sur deux à une heure donnée. Pour chaque mardi, c'est facile:

0 6 * * Tue

Mais comment le faire "tous les mardis sur deux" (ou si vous préférez - toutes les deux semaines)?.

51
tkokoszka

Que diriez-vous de cela, il le garde dans la crontab même s'il n'est pas défini exactement dans les cinq premiers champs:

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

Réponse

Modifiez la logique de votre cron de mardi pour qu’elle s’exécute toutes les deux semaines depuis l’époque.

Sachant qu'il y a 604800 secondes dans une semaine (en ignorant les modifications de l'heure d'été et les secondes intercalaires, merci), et en utilisant la date de GNU]:

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

De côté

L'arithmétique de calendrier est frustrante.

La réponse de @ xahtep est excellente, mais comme @Doppelganger l'a noté dans des commentaires , elle échouera à certaines années. Aucun des spécificateurs "semaine de l'année" de date utility ne peut aider ici. Certains mardi de début janvier répéteront inévitablement la parité hebdomadaire du dernier mardi de l'année précédente: 2016-01-05 (% V), 2018-01-02 (% U) et 2019-01-01 (% W) .

41
pilcrow

La réponse de pilcrow est géniale. Cependant, il en résulte que le script bimensuel.sh s'exécute toutes les même semaines (depuis l'époque). Si vous avez besoin que le script s'exécute odd semaines, vous pouvez modifier un peu sa réponse:

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

Changer le 1 en 0 le ramènera à des semaines paires.

7
notorious.dds

Peut-être un peu bête, mais on pourrait aussi créer deux tâches cron, une pour chaque premier mardi et une pour chaque troisième.

Premier cronjob:

0 0 8 ? 1/1 TUE#1 *

Deuxième cronjob:

0 0 8 ? 1/1 TUE#3 *

Pas sûr de la syntaxe ici, j'ai utilisé http://www.cronmaker.com/ pour les créer.

2
Hans

J'ai découvert certaines limitations supplémentaires des approches ci-dessus qui peuvent échouer dans certains cas Edge. Par exemple, considérons:

@xahtep et @Doppelganger ont discuté de problèmes en utilisant %W sur certaines limites d'année ci-dessus.

La réponse de @ pilcrow résout ce problème dans une certaine mesure, mais elle échouera également sur certaines limites. Les réponses à ce sujet et à d'autres sujets associés utilisent le nombre de secondes d'une journée (par rapport à la semaine), qui échouent également sur certaines limites pour les mêmes raisons.

En effet, ces approches s'appuient sur l'heure UTC (date +% s). Prenons un cas où nous travaillons à 1 heure du matin et à 22 heures le deuxième mardi.

Supposons que GMT-2:

  • 1h du matin heure locale = 23h UTC hier
  • 22h heure locale = 20h UTC aujourd'hui

Si nous ne vérifions qu'une seule fois par jour, cela ne posera pas de problème, mais si nous vérifions plusieurs fois - ou si nous sommes proches de l'heure UTC et que l'heure avancée a lieu, le script ne considérerait pas qu'il s'agit là du même jour.

Pour résoudre ce problème, nous devons calculer un décalage par rapport à UTC en nous basant sur notre fuseau horaire local et non sur UTC. Comme je ne pouvais pas trouver un moyen simple de faire cela dans BASH, j'ai donc développé une solution qui utilise une ligne rapide en Perl pour calculer le décalage par rapport à l'UTC en quelques secondes.

Ce script tire parti de la date +% z, qui génère le fuseau horaire local.

Script Bash:

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 ))

ensuite, pour déterminer si le jour est pair ou impair:

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

Qu'en est-il toutes les 3 semaines?

Voici ma suggestion:

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

... ou ...

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

... ou bien sur ...

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

... en fonction de la rotation de la semaine.

0
macetw

Si vous voulez le faire en fonction d'une date de début donnée:

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

Devrait congédier un lundi sur deux à compter du 2018-03-19

Expression lit: Run à 6h du matin si ...

1 - Obtenir la date du jour, en secondes, divisée par le nombre de secondes d'une journée à convertir en jours sice

2 - Faites la même chose pour la date de début, en la convertissant en nombre de jours depuis Epoch

3 - Obtenez la différence entre les deux

4 - diviser par 14 et vérifier le reste

5- Si le reste est à zéro, vous êtes sur le cycle de deux semaines

0
Paul Lemmons