53

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.

2

18 Answers 18

55

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.

5
  • 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 :/ May 29, 2012 at 8: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, 2012 at 8: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 May 29, 2012 at 9:33
  • Oh, now I see why. Glad it works now, thanks for trying it out!
    – slhck
    May 29, 2012 at 9:35
  • 1
    Beware that for alpine's CRON implementation, the percent sign needs not to be escaped.
    – helvete
    Feb 17, 2021 at 14:55
26

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!"
1
  • 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, 2018 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

1
  • 1
    Day 6 of week is Saturday not Friday.
    – Robert
    Jun 5, 2019 at 9:23
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
1
  • Would have the same effect as 0 16 22-28 * * , but would run four times a month more. Mar 7 at 5:53
5

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

2
  • 1
    This is not cron, it's possibly quartz
    – sivann
    Nov 29, 2019 at 7:16
  • 1
    According to crontab.cronhub.io it is cron. Can also try 0 0 8 ? * 1#1 *
    – dbinott
    Oct 26, 2022 at 15:12
2

I recommend to use

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

instead of

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

to avoid locale problem.

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
1
  • 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. May 25, 2012 at 15:56
1

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

[ `date +\%a` = "Sat" ] && echo "It's Saturday"
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.

1
  • +1 more success hit ration then to run for the first week and check for Monday. :-)
    – xmedeko
    Mar 22, 2017 at 7:28
1

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

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

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.

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

1
  • 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.
    – Community Bot
    Jan 7 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
1
  • Is it some cron dialect? What sort of cron is it for? This fails check in vixie-cron. Mar 7 at 6:17
-1

I believe this solves the problem more elegantly:

30 8 1-7 * 1 /run/your/job.sh
4
  • 1
    Doh! : "If both fields are restricted (ie, aren't *), the command will be run when either field matches the current time."
    – symcbean
    Jul 10, 2014 at 8:35
  • Also on February (the one that ends 28th) it will not be run every 4 years.
    – igraczech
    Oct 5, 2016 at 11:00
  • "either" means any of them, not "both". this will run every day for the 1st 7 days of the month.
    – minusf
    Aug 30, 2018 at 14:42
  • 1
    This row would run a command on the first to seventh of each month, as well as on every Monday Nov 30, 2018 at 5:23
-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

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, 2021 at 7:24
-2
0 9 1-7 * 1 * 

This will work for every monday of every month.

1
  • An answer has been accepted, is your answer better, different or useful on a newer version, etc.
    – mic84
    Sep 4, 2018 at 9:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.