57

I'd like to run a job from cron at 8.30 on the first Monday of every month. The cron Wikipedia page says

While normally the job is executed when the time/date specification fields all match the current time and date, there is one exception: if both "day of month" and "day of week" are restricted (not "*"), then either the "day of month" field (3) or the "day of week" field (5) must match the current day.

(my emphasis)

Does that mean I can't do the first Monday of the month, I can only do the first (or whatever) day of the month? I can't think of a way round it.

slhck
  • 223,558
  • 70
  • 607
  • 592
Max Williams
  • 2,917
  • 6
  • 29
  • 38
  • slhck - that sounds like a good solution - would you mind expanding it into an answer with the full code? then i'll mark it as correct :) – Max Williams May 28 '12 at 08:51
  • Similar answer in https://superuser.com/questions/1042805/how-to-set-cron-expression-to-run-first-weekday-mon-fri-and-first-weekend-sat – Michael Freidgeim May 01 '21 at 21:15

18 Answers18

57

You can put the condition into the actual crontab command (generic way):

[ "$(date '+%u')" = "1" ] && echo "It's Monday"

if your locale is EN/US, you can also compare strings (initial answer):

[ "$(date '+%a')" = "Mon" ] && echo "It's Monday"

Now, if this condition is true on one of the first seven days in a month, you have its first Monday. Note that in the crontab, the percent-syntax needs to be escaped though (generic way):

0   12  1-7 *   *   [ "$(date '+\%u')" = "1" ] && echo "It's Monday"

if your locale is EN/US, you can also compare strings (initial answer):

0   12  1-7 *   *   [ "$(date '+\%a')" = "Mon" ] && echo "It's Monday"

Replace the echo command with the actual command you want to run. I found a similar approach too.

4wk_
  • 165
  • 10
slhck
  • 223,558
  • 70
  • 607
  • 592
  • actually, slhck, i spoke too soon - this isn't working in my crontab: it seems to have a problem with the date test for some reason. This works in the command line `[ "$(date '+%a')" = "Mon" ] && echo "It's Monday"` as does `[ $(date '+%a') = "Mon" ] && echo "It's Monday"` But, in the crontab it seems to be failing at this test. I can't get at any useful feedback from cron either: i'm trying this `[ "$(date '+%a')" = "Tue" ] >> /home/deploy/cron.log 2>&1` to capture any error output into a log but that's not working either :/ – Max Williams May 29 '12 at 08:53
  • Hm. Can you check whether the `date` output is correct? For example, just let a `cron` command run that outputs this to a file: `date '+%a' > ~/datetest 2>&1` – then check if that resulted in the right string to compare against. – slhck May 29 '12 at 08:58
  • 3
    I found the problem - the percent in `+%a` needed escaping when it was in the crontab, ie the line needed to be this: `[ "$(date '+\%a')" = "Mon" ] && echo "It's Monday"`. If you could update your answer with that it would be helpful to future readers :) Thanks again - max – Max Williams May 29 '12 at 09:33
  • Oh, now I see why. Glad it works now, thanks for trying it out! – slhck May 29 '12 at 09:35
  • 1
    Beware that for alpine's CRON implementation, the percent sign **needs not** to be escaped. – helvete Feb 17 '21 at 14:55
28

I have a computer with locale on Spanish, so, this approach isn't working for me because mon changes to lun

Other languages would fail as well, so, I did a slight variation on the accepted answer that takes out the language barrier:

 0 9 1-7 * *   [ "$(date '+\%u')" = "1" ] && echo "¡Es lunes!"
cumanacr
  • 381
  • 3
  • 4
  • 1
    This should be the accepted answer. I have a Dutch locale which also uses different names for weekdays. And using numbers also feels cleaner than comparing strings to determine the day of the week. – 0ne_Up Nov 28 '18 at 10:54
11

I find it easier when there's no need to handle day numbers.

Run First Monday of month:

0 2 * * 1 [ `date '+\%m'` == `date '+\%m' -d "1 week ago"` ] || /path/to/command

i.e. if the month 1 week ago is not the same as the current month then we are on the 1st day 1 (= Monday) of the month.

Similarly, for the Third Friday

0 2 * * 6 [ `date '+\%m'` == `date '+\%m' -d "3 weeks ago"` ] || /path/to/command

i.e. if the month 3 weeks ago is different to current month then we are on the 3rd day 6 (= Friday) of the month

Joel
  • 3
  • 2
joel
  • 111
  • 1
  • 2
7

Since I interpret my cron statements using php and js, I can't use bash. Finally I found that it is in fact possible with just cron:

0 30 8 * 1/1 MON#1

Hope this helps someone else. Regardless, I wish you all a beautiful day. :-)

kramer65
  • 1,394
  • 4
  • 21
  • 43
5

I have scheduled a job to run on the 4th Monday of every month at 4:00 PM as follows:

0 16 22-28 * Mon [ "$(date '+\%a')" == "Mon" ] && touch /home/me/FourthMonOfMonth.txt
Indrek
  • 24,204
  • 14
  • 90
  • 93
3

There is a hacky way to do this with a classic (Vixie, Debian) cron:

30 8 */100,1-7 * MON

The day-of-month field starts with a star (*), and so cron considers it "unrestricted" and uses the AND logic between the day-of-month and the day-of-week fields.

*/100 means "every 100 days starting from date 1". Since there are no months with more than 100 days, */100,1-7 effectively means "on dates 1 to 7".

Here's my article with more details: Schedule Cronjob for the First Monday of Every Month, the Funky Way

cuu508
  • 262
  • 1
  • 3
  • 10
2

I recommend to use

"$(/bin/date '+%\w')" = "1"

instead of

"$(date '+\%a')" = "Mon"

to avoid locale problem.

Tomasz Jakub Rup
  • 632
  • 5
  • 14
1

As far as I know it is NOT possible using only crontab, however one can use a wrapper function to pick the correct day from a "first seven days of month" contab entry; see this from entry.

The wrapper script would be

#! /usr/bin/ksh
day=$(date +%d)
if ((day <= 7)) ; then
   exec somecommand
fi
exit 1

and you would need to run it (assuming it is called wrapper.sh and globally available) using the crontab entry

0 0 * * 1 wrapper.sh
elemakil
  • 301
  • 2
  • 11
  • thanks elemaki. I thought about a similar solution using some extra code inside the task that gets run, getting it to test whether it's the first monday of the month inside the script, and then calling it every monday in crontab. It's a ruby script so it's easy to test the day of th week. But i was hoping there'd be a crontab only way. – Max Williams May 25 '12 at 15:56
1

On Solaris 10 I had to format the condition as follows:

[ `date +\%a` = "Sat" ] && echo "It's Saturday"
Will
  • 11
  • 2
1

You can try running cronjob for first seven days of month and let it execute only on Monday.

30 8 * * 1 [`date +\%d` -le 07] && <job>

Above should work for you.

simer
  • 173
  • 1
  • 8
0

This usage should be most universal and avoids the locale issue.

[ `/bin/date +\%u` -eq 1 ]

the first Monday of the month at 6am would look like this in /etc/crontab

00 6 1-7 * *    root    [ `/bin/date +\%u` -eq 1 ] && /run/yourjob.sh
0
crontab 30 8 */27 * 1

At 08:30 AM, [ones] every 27 days, and on Monday (see crontab expression generator)

Day of the month is unencumbered (is *), so the either logic/exception does not apply.

SRGDVD
  • 1
  • 1
0

The day of month field here is */100,1-7, meaning “every 100 days starting from date 1, and also on dates 1-7”. Since there are no months with 100+ days, this again is a trick to say “on dates 1 to 7” but with a leading star. Because of the star, cron will run the command on dates 1 to 7 that are also Monday.

0 22 */100,1-7 * 2

On the cron guru example below, you can see the result and the next run date.

https://crontab.guru/#0_0_/100,1-7__MON

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 07 '23 at 21:02
0

I just ran into this and here is what I came up with:

0 11 * * 1#1 - Run at 11 am on the first Monday of every month
0 11 * * 1#2 - Run at 11 am on the second Monday of every month
0 11 * * 1#3 - Run at 11 am on the third Monday of every month

If you wanted to run on Tuesday instead:

0 11 * * 2#1 - Run at 11 am on the first Tuesday of every month
0 11 * * 2#2 - Run at 11 am on the second Tuesday of every month
0 11 * * 2#3 - Run at 11 am on the third Tuesday of every month
Dominique
  • 1,993
  • 9
  • 30
  • 65
ErnieAndBert
  • 111
  • 2
0

I made a general solution for this kind of problems, it works for first, second, third..... last weekday of the month.

You can use it like this:

30 06 * * Mon run-if-today 1 "Mon" && echo "First Monday"
30 06 * * Thu run-if-today 3 "Thu" && echo "Third Thursday"
30 06 * * Sun run-if-today L "Sun" && echo "Last Sunday"

The script run-if-today check for both the weekday and the desired week date range, if both match then it returns 0, otherwise 1.

Check the code here. https://github.com/xr09/cron-last-sunday

MGP
  • 111
  • 3
-1

I needed the same result, but wanted to use pure cron. I hope this improves on the accepted answer.

For me I needed it to run the First Monday of each month at noon.

So this should run: At Noon, in the first 7 days of the month, on a Monday: 0 12 1-7 * 1

https://crontab.guru/#0_12_1-7_*_1

  • from the guru: `“At 12:00 on every day-of-month from 1 through 7 and on Monday.”` it will run every day of the week AND on Monday. – Art3mix Sep 24 '21 at 07:24
-1

I believe this solves the problem more elegantly:

30 8 1-7 * 1 /run/your/job.sh
symcbean
  • 271
  • 1
  • 9
-2
0 9 1-7 * 1 * 

This will work for every monday of every month.

SIBHI S
  • 119
  • 4
  • An answer has been accepted, is your answer better, different or useful on a newer version, etc. – mic84 Sep 04 '18 at 09:32