CRON 作业在该月的最后一天运行

Posted

技术标签:

【中文标题】CRON 作业在该月的最后一天运行【英文标题】:CRON job to run on the last day of the month 【发布时间】:2011-09-02 14:37:52 【问题描述】:

我需要创建一个在每个月的最后一天运行的 CRON 作业。 我将使用 cPanel 创建它。

感谢任何帮助。 谢谢

【问题讨论】:

【参考方案1】:

可能最简单的方法是简单地做三个独立的工作:

55 23 30 4,6,9,11        * myjob.sh
55 23 31 1,3,5,7,8,10,12 * myjob.sh
55 23 28 2               * myjob.sh

不过,这将在 2 月 28 日运行,即使是闰年,所以如果这是个问题,您需要找到其他方法。


但是,在每个月的第一天尽快运行作业通常既容易又正确,例如:

0 0 1 * * myjob.sh

并修改脚本以处理个月的数据。

这消除了您在确定一个月的最后一天时可能遇到的任何麻烦,并确保该月的所有数据都可用(假设您正在处理数据)。在当月最后一天的午夜前五分钟跑步可能会让您错过从那时到午夜之间发生的任何事情。

无论如何,对于大多数月底的工作来说,这都是通常的做法。


如果您仍然真的想在当月的最后一天运行它,一个选择是简单地检测明天是否是第一天(作为脚本的一部分,或者在 crontab 本身中) )。

所以,类似:

55 23 28-31 * * [[ "$(date --date=tomorrow +\%d)" == "01" ]] && myjob.sh

应该是一个好的开始,假设你有一个相对智能的date程序。

如果您的 date 程序还不够先进,无法为您提供相对日期,您可以编写一个非常简单的程序来为您提供每月的明天(您不需要 fulldate) 的幂,如:

#include <stdio.h>
#include <time.h>

int main (void) 
    // Get today, somewhere around midday (no DST issues).

    time_t noonish = time (0);
    struct tm *localtm = localtime (&noonish);
    localtm->tm_hour = 12;

    // Add one day (86,400 seconds).

    noonish = mktime (localtm) + 86400;
    localtm = localtime (&noonish);

    // Output just day of month.

    printf ("%d\n", localtm->tm_mday);

    return 0;

然后使用(假设您将其称为 tomdom 表示“每月的明天”):

55 23 28-31 * * [[ "$(tomdom)" == "1" ]] && myjob.sh

尽管您可能需要考虑添加错误检查,因为如果出现问题,time()mktime() 都可以返回 -1。为简单起见,上面的代码没有考虑到这一点。

【讨论】:

是的,在第一天而不是最后一天运行会更容易:) 确实是每月的第一天。下面是代码在 php 中的样子 $date = new DateTime('2013-03-01'); $date->modify('-1 月'); $previousMonth = $date->format('Y-m'); // $previousMonth 现在是 2013-02。构建查询以获取上个月的产品。 闰年 2 月 29 日的数据会丢失,我们也需要考虑这一点。下面的 Thunder Rabbit 答案考虑到这一点,但 cron 在闰年 2 月运行两次 @Hari,首选解决方案是在每月的第一天运行并收集上个月的数据。在这种情况下不会错过 2 月 29 日。 考虑闰年:如果你不介意它运行两次:55 23 28,29 2 * myjob.sh【参考方案2】:

有一种略短的方法可以使用,类似于上述方法之一。那就是:

[ $(date -d +1day +%d) -eq 1 ] && echo "last day of month"

此外,crontab 条目可以更新为仅在 28 日至 31 日检查,因为在该月的其他日子运行它是没有意义的。这会给你:

0 23 28-31 * * [ $(date -d +1day +%d) -eq 1 ] && myscript.sh

【讨论】:

我无法让它作为 crontab 条目工作(我认为需要转义一些东西)。但是,它在从 crontab 调用的 shell 脚本中运行良好。仅供参考,我得到的错误是/bin/sh: -c: line 1: unexpected EOF while looking for matching ')' 这很好用。在 crontab 文件中,必须对 % 进行转义。所以[ $(date -d +1day +\%d) -eq 1 ] &amp;&amp; run_job 确实不错!但问题被标记为posix 和POSIX date 不支持"-d +1day" :-\ 更复杂(和丑陋)的解决方案是:[ `date +\%d` -eq `cal | xargs echo | awk 'print $NF'` ] &amp;&amp; myscript.sh【参考方案3】:

***之后的这个呢?

55 23 L * * /full/path/to/command

【讨论】:

嗯,怎么样?即:“crontab 文件中出现错误的日期错误,无法安装。您要重试相同的编辑吗?” 为了清楚起见,***条目还提到“L”是非标准的。【参考方案4】:

设置一个 cron 作业以在每月的第一天运行。然后将系统时钟更改为提前一天。

【讨论】:

这意味着系统时钟总是错误的。抱歉,但我认为这会导致更多问题。那么-1。 现在我知道伽利略的感受了。 @iconoclast:我认为它更像是一个公案而不是一个笑话。 我认为这是一个非常糟糕但绝妙的主意。孩子们,不要在家里这样做。 @pascalbetz 哦,人们可以在家里这样做,但他们真的不应该在工作中这样做。【参考方案5】:

适应 paxdiablo 的解决方案,我在 2 月 28 日和 2 月 29 日运行。 29 日的数据会覆盖 28 日的数据。

# min  hr  date     month          dow
  55   23  31     1,3,5,7,8,10,12   * /path/monthly_copy_data.sh
  55   23  30     4,6,9,11          * /path/monthly_copy_data.sh
  55   23  28,29  2                 * /path/monthly_copy_data.sh

【讨论】:

当然,除非该作业具有一些破坏性方面,例如在处理时清除所有数据 :-) 这实际上是一个无痛的选择(技术性最低),如果您忽略 ,29,您可能不会在意 4 年内三次您会过早获得 cronjob。 @Matt:嗯,如果你的 crontab 条目显示 @,你的意思是不是太早了一天 一次 987654323@? @G-Man 是的,你是对的,最重要的是,你让它在 29 日第二次运行。【参考方案6】:

对于 AWS Cloudwatch cron 实施(调度 Lambda 等),此方法有效:

55 23 L * ? *

每月最后一天晚上 11:55 运行。

【讨论】:

【参考方案7】:

您可以设置一个 cron 作业在每个月的每一天运行,并让它运行如下所示的 shell 脚本。这个脚本计算出明天的天数是否小于今天(即明天是新的一个月),然后做任何你想做的事情。

TODAY=`date +%d`
TOMORROW=`date +%d -d "1 day"`

# See if tomorrow's day is less than today's
if [ $TOMORROW -lt $TODAY ]; then
echo "This is the last day of the month"
# Do stuff...
fi

【讨论】:

我不确定你通过检查它是否比今天少获得了什么 - 你应该能够检查它是否是第一个。除非每月的第一天可以是第二天或第三天:-)【参考方案8】:

对于基于@Indie 解决方案的 crontab 中更安全的方法(使用date + $() 的绝对路径不适用于所有 crontab 系统):

0 23 28-31 * * [ `/bin/date -d +1day +\%d` -eq 1 ] && myscript.sh

【讨论】:

【参考方案9】:

一些 cron 实现支持“L”标志来表示该月的最后一天。

如果您有幸使用其中一种实现方式,那么它很简单:

0 55 23 L * ?

这将在每个月的最后一天晚上 11:55 运行。

http://www.quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger

【讨论】:

【参考方案10】:
#########################################################
# Memory Aid 
# environment    HOME=$HOME SHELL=$SHELL LOGNAME=$LOGNAME PATH=$PATH
#########################################################
#
# string         meaning
# ------         -------
# @reboot        Run once, at startup.
# @yearly        Run once a year, "0 0 1 1 *".
# @annually      (same as @yearly)
# @monthly       Run once a month, "0 0 1 * *".
# @weekly        Run once a week, "0 0 * * 0".
# @daily         Run once a day, "0 0 * * *".
# @midnight      (same as @daily)
# @hourly        Run once an hour, "0 * * * *".
#mm     hh      Mday    Mon     Dow     CMD # minute, hour, month-day month DayofW CMD
#........................................Minute of the hour
#|      .................................Hour in the day (0..23)
#|      |       .........................Day of month, 1..31 (mon,tue,wed)
#|      |       |       .................Month (1.12) Jan, Feb.. Dec
#|      |       |       |        ........day of the week 0-6  7==0
#|      |       |       |        |      |command to be executed
#V      V       V       V        V      V
*       *       28-31   *       *       [ `date -d +'1 day' +\%d` -eq 1 ] && echo "Tomorrow is the first today now is  `date`" >> ~/message
1       0       1       *       *       rm -f ~/message
*       *       28-31   *       *       [ `date -d +'1 day' +\%d` -eq 1 ] && echo "HOME=$HOME LOGNAME=$LOGNAME SHELL = $SHELL PATH=$PATH" 

【讨论】:

【参考方案11】:
00 23 * * * [[ $(date +'%d') -eq $(cal | awk '!/^$/ print $NF ' | tail -1) ]] && job

在 unix.com 论坛上查看related question。

【讨论】:

【参考方案12】:

您可以在一个 cron 行中连接所有答案并仅使用 date 命令。

只需检查今天和明天的月份之间的差异:

0 23 * * * root [ $(expr $(date +\%d -d '1 days') - $(date +\%d)  ) -le 0 ]  && echo true

如果这些差值小于 0,则意味着我们更改了月份,并且有该月的最后一天。

【讨论】:

【参考方案13】:
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash 

【讨论】:

回显命令并将其传送到 bash 是使用 cron 时的最佳方式。由于 cron 使用的是 sh 而不是 bash。检查您的 bash 在哪里使用“哪个 bash”。在 FreeBSD 上是 /usr/local/bin/bash,在 Linux 上是 /bin/bash。【参考方案14】:

这个呢?

编辑用户.bashprofile添加:

export LAST_DAY_OF_MONTH=$(cal | awk '!/^$/ print $NF ' | tail -1)

然后将此条目添加到crontab:

mm hh * * 1-7 [[ $(date +'%d') -eq $LAST_DAY_OF_MONTH ]] && /absolutepath/myscript.sh

【讨论】:

【参考方案15】:

每月的最后一天可以是 28-31,具体取决于月份(2 月、3 月等)。但是,在这两种情况下,第二天总是下个月的第一天。所以我们可以使用它来确保我们总是在一个月的最后一天运行一些工作,使用下面的代码:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh

【讨论】:

【参考方案16】:

如果日期字段可以接受第零天,那将非常简单地解决此问题。例如。天文学家用零日来表示上个月的最后一天。所以

00 08 00 * * /usr/local/sbin/backup

会以简单易行的方式完成这项工作。

【讨论】:

【参考方案17】:

我从this site 找到了如下解决方案(在每月的最后一天)。

 0 0 0 L * ? *

CRON 详细信息:

Seconds Minutes Hours   Day Of Month    Month   Day Of Week  Year
0       0       0       L               *       ?            *

要交叉验证上面的表达式,click here 会给出如下输出。

2021-12-31 Fri 00:00:00
2022-01-31 Mon 00:00:00
2022-02-28 Mon 00:00:00
2022-03-31 Thu 00:00:00
2022-04-30 Sat 00:00:00

【讨论】:

【参考方案18】:

不确定其他语言,但在 javascript 中是可能的。

如果您需要 在每月的第一天之前完成作业,node-cron 将允许您设置时区 - 您必须设置 UTC+12:00 并且如果作业时间不会太长的世界将在他们的月初之前获得结果。

【讨论】:

【参考方案19】:

在每个下个月的第一天安排 cron 的更好方法

这将在上午 12:00 运行命令 foo。

0 0 1 * * /usr/bin/foo

【讨论】:

这没有提供问题的答案。一旦你有足够的reputation,你就可以comment on any post;相反,provide answers that don't require clarification from the asker。 - From Review

以上是关于CRON 作业在该月的最后一天运行的主要内容,如果未能解决你的问题,请参考以下文章

SQL 查询以查找该月的最后一天

在给定的字符串日期中获取该月的最后一天

如何使用 Python 仅查找包含该月最后一天的日期的文件?

如何从此字符串'opimus_rise_issue_command_201912.txt'中提取该月的最后一天日期

asp.net后台获得本月第一天和最后一天

to_date ora-01847 月份中的某天必须介于 1 和该月的最后一天之间