How to instruct cron to execute a job every second week?

∥☆過路亽.° 提交于 2019-11-26 18:57:19
xahtep

How about this, it does keep it in the crontab even if it isn't exactly defined in the first five fields:

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

Answer

Modify your Tuesday cron logic to execute every other week since the epoch.

Knowing that there are 604800 seconds in a week (ignoring DST changes and leap seconds, thank you), and using GNU date:

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

Aside

Calendar arithmetic is frustrating.

@xahtep's answer is terrific but, as @Doppelganger noted in comments, it will fail on certain year boundaries. None of the date utility's "week of year" specifiers can help here. Some Tuesday in early January will inevitably repeat the week parity of the final Tuesday in the preceding year: 2016-01-05 (%V), 2018-01-02 (%U), and 2019-01-01 (%W).

notorious.dds

pilcrow's answer is great. However, it results in the fortnightly.sh script running every even week (since the epoch). If you need the script to run on odd weeks, you can tweak his answer a little:

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

Changing the 1 to a 0 will move it back to even weeks.

Maybe a little dumb, but one could also create two cronjobs, one for every first tuesday and one for every third.

First cronjob:

0 0 8 ? 1/1 TUE#1 *

Second cronjob:

0 0 8 ? 1/1 TUE#3 *

Not sure about the syntax here, I used http://www.cronmaker.com/ to make these.

I discovered some additional limitations of above approaches that can fail in some edge cases. For instance, consider:

@xahtep and @Doppelganger discussed issues using %W on certain year boundaries above.

@pilcrow's answer gets around this to some degree, however it too will fail on certain boundaries. Answers in this and or other related topics use the number of seconds in a day (vs. week), which also fail on certain boundaries for the same reasons.

This is because these approaches rely on UTC time (date +%s). Consider a case where we're running a job at 1am and 10pm every 2nd Tuesday.

Suppose GMT-2:

  • 1am local time = 11pm UTC yesterday
  • 10pm local time = 8pm UTC today

If we are only checking a single time each day, this will not be an issue, but if we are checking multiple times -- or if we are close to UTC time and daylight savings occurs, the script wouldn't consider these to be the same day.

To get around this, we need to calculate an offset from UTC based on our local timezone not UTC. I couldn't find a simple way to do this in BASH, so I developed a solution that uses a quick one liner in Perl to compute the offset from UTC in seconds.

This script takes advantage of date +%z, which outputs the local timezone.

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

then, to determine whether the day is even or odd:

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

If you want to do it based on a given start date:

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

Should fire every other Monday beginning with 2018-03-19

Expression reads: Run at 6am on Mondays if ...

1 - Get today's date, in seconds, divided by the number of seconds in a day to convert to days sice epoch

2 - Do the same for the starting date, converting it to the number of days since epoch

3 - Get the difference between the two

4 - divide by 14 and check the remainder

5- If the remainder is zero you are on the two-week cycle

macetw

What about every 3rd week?

Here's my suggestion:

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

... or ...

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

... or of course ...

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

... depending on the week rotation.

If you want every tuesday of second week only:

00 06 * * 2#2

Syntax "/2" is not goes along with weekday. So my added to the smart above is simply use 1,15 on MonthDay filed.

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

Why not something like

0 0 1-7,15-21,29-31 * 5

Is that appropriate?

Cron provides an 'every other' syntax "/2". Just follow the appropriate time unit field with "/2" and it will execute the cronjob 'every other time'. In your case...

0 6 * * Tue/2

The above should execute every other Tuesday.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!