12

Today is Friday, October 3, 2014 3:58 AM

I want to schedule a cronjob like that to run it at the following dates:

  1. Saturday, October 4, 2014 8:00 AM
  2. Saturday, October 18, 2014 8:00 AM
  3. Saturday, November 1, 2014 8:00 AM ... ...

So every 2 weeks , on Saturday, at 8 o'clock.

kupa
  • 381
  • 2
  • 8
  • 18

2 Answers2

29
0 8 * * 6 test $((10#$(date +\%W)\%2)) -eq 1 && yourCommand

date +%W: week number of year with Monday as first day of week, today week 39

10#$(date +%W): conver the date +W to decimal number and avoid shell base parsing confusion

$((39%2)): modulo operation: result is 0 (even week number) or 1 (odd week number), this week result is 1, next week 0

test 1 -eq 1: arithmetic test (equal), in this case result is boolean true

&& yourCommand: Boolean AND: run yourCommand only if result of previous command was boolean true

Note that the year can get two odd weeks: 53 (this year) and 1 (next year)

Cyrus
  • 890
  • 1
  • 7
  • 15
  • 2
    That's elegant! It still, as you've noted, has a corner-case on the 53rd Saturday of the year, which will happen roughly seventy years in every four centuries. – MadHatter Oct 03 '14 at 10:11
  • @Cyrus thank you for your answer, but if I write * * * * 5 test $(($(date +%W)%2)) -eq 1 && /u02/restore/scripts/test.sh the script does not run but if I write * * * * 5 /u02/restore/scripts/test.sh script runs. why is your expression not working? I was just testing the cases.And found that not working somehow. – kupa Oct 03 '14 at 12:32
  • 1
    My mistake. Cron interprets % as newline. Escape both % with a \ in your cronjob: `0 8 * * 6 test $(($(date +\%W)\%2)) -eq 1 && yourCommand` – Cyrus Oct 03 '14 at 13:07
  • Sorry to bring back this old question but it wasn't working for me and when i try to execute `$((10#$(date +%W)%2)) -eq 1 && echo OK` my shell tries to execute the result of the week calculation : `-bash: 1: command not found`. Any clue why it's doing that ? Thanks. – jhuet Mar 31 '17 at 12:35
  • 3
    @jhuet: `test` is no user it‘s a command. Try: `test $((10#$(date +\%W)%2)) -eq 1 && echo odd || echo even` – Cyrus Mar 31 '17 at 18:23
  • Oooh, you're right, i definitely thought it was a user :) Thanks for the help! – jhuet Apr 01 '17 at 21:31
  • `/bin/sh: 1: arithmetic expression: expecting EOF: "10#11%2"` – Redsandro Mar 19 '21 at 16:46
5

What you've shown is "every week". Then the code is:

0 8 * * 6

Are you sure you need to run it every two weeks?

0 8 * * 6 expr `date +\%s` / 604800 \% 2 >/dev/null || yourCommand
Glueon
  • 3,514
  • 2
  • 22
  • 31
  • 1
    Can you explain the command, please – kupa Oct 03 '14 at 09:39
  • 1
    It's better to use what Cyrus gave - $(($(date +%W)%2)) "W" stands for a week number the year. Starting from zero to 53. If it can be divided by two, then it's your "every second" week. So cronjob runs every week but the 'expr' executes command yourCommand only if week's number can be divided by 2. – Glueon Oct 03 '14 at 09:44
  • when I run test $(($(date +%W)%2)) -eq 1 on command line returns nothing, why? – kupa Oct 03 '14 at 09:52
  • 1
    just run `echo $?` after running that command, to see its return code -- that's what is important with it – Ale Oct 03 '14 at 09:55
  • `expr` says evaluate the following expression. `date +%s` returns the number of seconds since the Unix Epoch (01/01/1970 00:00:00). `604800` is the number of seconds in 7 days (86,400 sec/day) i.e. number of epoch weeks. `%2` is modulo arithmetic, returns 0 if even 1 if odd. `|| command` or `&& command` will determine if the command runs on an odd or even week. This algorithm accuratel handles the 51 or 53 weeks/year problem since it uses Epoch weeks and not Week of the Year. – Scottie H Aug 24 '21 at 22:17