如何定义在每个月的第一个星期一执行脚本的 cron 表达式?

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

我有一个这样的脚本:

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

我需要在每个月的第一个星期一定义每月一次的cron表达式“* * * * *”到运行。

crontab 语法无法定义所有可能的 人们可以想象的时期。例如,它不是直截了当的 如您所述,定义一个月的最后一个工作日或每个月的第一个星期一。

那么,最好的办法就是让script.sh检查一下。 使用这样的东西:

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

每月的第一个星期一是该月前 7 天中唯一的星期一。因此,要在 1:02 AM 执行作业,请使用:

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

前两项,数字12,设置分钟和小时。

第三项,1-7,设置一个月中某天的允许范围。

第四项 * 是月份,我们允许所有月份匹配。

第五项*设置星期几。 此字段与日期字段进行“或”运算。因此,在此处指定星期一没有用。如果我们这样做,cronjob 将在每个月的前 7 天 所有星期一 运行。

最后,我们有 test $(date +\%u) -eq 1。这 returns 仅在星期一为真。由于紧随其后的 &&,您的脚本将仅在每月前 7 天的星期一执行。

请注意,在 crontab 条目中,% 被视为换行符,除非它被转义。因为我们需要一个文字 %,所以在上面的命令中用反斜杠转义了

有关其工作原理的更多信息,请参阅 man 5 crontab

备选

正如 twalberg 所指出的,同样的逻辑反过来也适用。我们可以 运行 每个星期一的 cronjob,然后测试那个星期一是否是该月的前 7 天之一:

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

如果您使用 Debian 或 Debian 衍生产品附带的 cron,您可以使用:

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

是的,它有效,但有点难以解释为什么。

首先,让我们考虑这个表达式:

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

这个将 运行 在日期 1-7 以及每周一举行。这几乎就是我们想要的,除了我们想要 day-of-month 和 day-of-week 字段之间的“AND”关系,而不是“OR”关系。

奇怪的额外 */50 位利用了 Debian cron 的表达式解析逻辑中的一个怪癖。它使 cron 认为 day-of-month 字段是通配符字段,并应用“AND”逻辑。

注意 – 这是一个 hack。如果您使用此表达式,请确保将其记录下来以避免以后混淆。