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


95

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

任何帮助表示赞赏。谢谢

Answers:


183

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

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

不过,即使是leap年,也要在2月28日进行,如果这是一个问题,则需要寻找其他方法。


但是,通常在每个月的第一天尽快进行这项工作既容易又正确,实际上是这样的:

0 0 1 * * myjob.sh

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

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

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


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

因此,类似:

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

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

如果您的date程序不够先进,无法为您提供相对日期,则可以将一个非常简单的程序组合在一起,即可为您提供明天的明天(您不需要的全部功能date),例如:

#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。为了简单起见,上面的代码没有考虑到这一点。


7
是的,在第一天而不是最后一天运行起来可能会更容易:)
Utku Dalmaz

1
确实是每月的第一天。这是代码在PHP中的样子$ date = new DateTime('2013-03-01'); $ date-> modify('-1 month'); $ previousMonth = $ date-> format('Y-m'); // $ previousMonth现在是2013-02。建立查询以获取上个月的产品。
拉米

年2月29日的数据将丢失,我们也需要考虑这一点。下面的“雷兔”答案认为,但cron在leap年2月运行了两次
Hari Swaminathan 2014年

1
@Hari,首选解决方案是在每月的第一天运行并收集前一个月的数据。在这种情况下,不会错过2月29日。
paxdiablo 2014年

1
考虑leap年:如果您不介意它运行两次:55 23 28,29 2 * myjob.sh
radiantRazor

52

与上述方法之一类似,可以使用一种稍短的方法。那是:

[ $(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 ')'
马克Rajcok

13
这很好。在crontab文件中,必须对%进行转义。所以[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM 2014年

确实不错!但是问题被标记了posixPOSIX日期不支持"-d +1day":-\一个更复杂(又丑陋)的解决方案是:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau 2015年

15

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


11
这意味着系统时钟总是错误的。抱歉,但是我认为这会引起更多问题。-1然后
鲁迪(Rudy)

70
现在我知道伽利略的感觉了。
Tom Anderson

7
@iconoclast:我想把它更多的是可口可乐而不是笑话。
Tom Anderson

14
我认为这是一个非常糟糕而绝妙的主意。孩子们,不要在家做这个。
Pascal,

11
@pascalbetz哦,人们可以在家中做到这一点,但他们真的不应该在工作中这样做。
汤姆·安德森

14

那在维基百科之后呢?

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

好吧,那呢?那是:“ crontab文件中每月的每日错误错误,无法安装。是否要重试相同的编辑?”
webjunkie 2012年

12
需要明确的是,维基百科条目还提到“ L”是非标准的。
sdupton

10

根据paxdiablo的解决方案,我于2月28 日 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

4
当然,除非这项工作具有某些破坏性的方面,例如在处理过程中清除所有数据:-)
paxdiablo

这实际上是一个无痛的选择(至少是技术上的选择),并且您可能不在乎4年内三次,如果您只是忽略了cronjob,那将太早了,29
马特

@Matt:嗯,你的意思是说如果crontab条目说的话,它一天就会提前四年运行一次55   23   28    2吗?
G-Man说'Resstate Monica''19

@ G-Man是的,您是对的,最重要的是,它在29日第二次运行。
马特

8

您可以设置一个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

我不确定通过检查是否比今天少而获得的收益-您应该只能检查它是否是第一名。除非每月的第一天可以是2号或3号:-)
paxdiablo

7

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

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


5
#########################################################
# 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" 

5

对于AWS Cloudwatch cron实施(计划Lambda等),这有效:

55 23 L * ? *

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


4
00 23 * * * [[ $(date +'%d') -eq $(cal | awk '!/^$/{ print $NF }' | tail -1) ]] && job

在unix.com论坛上查看相关问题


3

您只需将所有答案连接在一个cron行中,并仅使用date命令即可。

只需检查今天和明天之间的月份之间的差额即可:

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

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


3
55 23 28-31 * * echo "[ $(date -d +1day +%d) -eq 1 ] && my.sh" | /bin/bash 

1
在使用cron时,回显命令并将其通过管道传送到bash中是最好的方法。由于cron使用sh而不是bash。检查您的bash使用“哪一bash”。在FreeBSD上,它是/ usr / local / bin / bash,在Linux上是/ bin / bash。
唐老鸭

2

那这个呢?

编辑用户的.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

0

一个月的最后一天可以是28-31,具体取决于哪个月(2月,3月等)。但是,在这两种情况下,第二天始终是下个月的1号。因此我们可以使用以下代码来确保始终在一个月的最后一天运行某些作业:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.