'How to define a cron expression that execute a script in the first monday of each month?

I have a script such as that:

* * * * * path/to/my/script.sh

I need to define the cron expression " * * * * * " to run once a month in the first Monday of each month.



Solution 1:[1]

The first monday of the month is the only monday that occurs during the first seven days of the month. Thus, to execute the job at 1:02 AM, use:

2 1 1-7 * * test $(date +\%u) -eq 1 && path/to/my/script.sh

The first two items, the numbers 1 and 2, set the minute and hour.

The third item, 1-7, sets the allowed range for the day of the month.

The fourth item, *, is the month and we allow all months to match.

The fifth item, *, sets the day of the week. This field is OR'd with the day of the month field. Consequently, it is not useful to specify monday here. If we did, the cronjob would run on both the first seven days of the month and all mondays.

Lastly, we have test $(date +\%u) -eq 1. This returns True only on mondays. Because of the && which follows it, your script will be executed only on the monday that occurs during the first seven days of the month.

Note that, in crontab entries, % is treated as a newline unless it is escaped. Because we need a literal %, it is escaped with a backslash in the command above.

For more on how this works see man 5 crontab.

Alternative

As twalberg points out, the same logic works the other way around. We can run the cronjob on every Monday and then test if that monday falls on one of the first seven days of the month:

2 1 * * Mon  test $(date +\%e) -le 7 && path/to/my/script.sh

Solution 2:[2]

If you use cron that ships with Debian or a Debian derivative, you can use this:

0 0 */50,1-7 * MON path/to/my/script.sh    

Yes, it works, but it is a little hard to explain why.

First, let's consider this expression:

0 0 1-7 * MON path/to/my/script.sh    

This one will run on dates 1-7, and additionally on every Monday. This is almost what we want, except we want an "AND" relationship between the day-of-month and the day-of-week fields, instead of the "OR" relationship.

The weird extra */50 bit exploits a quirk in Debian cron's expression parsing logic. It fools cron into thinking the day-of-month field is a wildcard field, and into applying the "AND" logic.

Note – it's a hack. If you use this expression, make sure to document it to avoid confusion later.

Solution 3:[3]

The crontab syntax does not make it possible to define all possible periods one could image off. For example, it is not straightforward to define the last weekday of a month or the first Monday of each month as you mentioned.

Then, the best approach would be to make script.sh to check it. Use something like this:

if [ $(date +\%e) != $(ncal | awk '/Mo/ { print $2 }') ]
then
   exit
fi

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Diego Torres Milano