Công việc CRON sẽ chạy vào ngày cuối cùng của tháng


95

Tôi cần tạo một công việc CRON sẽ chạy vào ngày cuối cùng của mỗi tháng. Tôi sẽ tạo nó bằng cPanel.

Bất kỳ trợ giúp được đánh giá cao. Cảm ơn

Câu trả lời:


180

Có thể cách dễ nhất là làm ba công việc riêng biệt:

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

Tuy nhiên, điều đó sẽ diễn ra vào ngày 28 tháng 2, ngay cả vào những năm nhuận, vì vậy, nếu đó là một vấn đề, bạn sẽ cần phải tìm một cách khác.


Tuy nhiên, việc thực hiện công việc càng sớm càng tốt vào ngày đầu tiên của mỗi tháng thường dễ dàng và chính xác hơn rất nhiều , với những điều như:

0 0 1 * * myjob.sh

và sửa đổi kịch bản để xử lý trước dữ liệu tháng.

Điều này loại bỏ mọi phức tạp mà bạn có thể gặp phải khi tìm ra ngày nào là ngày cuối cùng của tháng và cũng đảm bảo rằng tất cả dữ liệu cho tháng đó đều có sẵn, giả sử bạn đang xử lý dữ liệu. Chạy vào lúc năm phút đến nửa đêm vào ngày cuối cùng của tháng có thể thấy bạn bỏ lỡ bất kỳ điều gì xảy ra từ sau đó đến nửa đêm.

Đây là cách thông thường để làm, đối với hầu hết các công việc cuối tháng.


Nếu bạn vẫn thực sự muốn chạy nó vào ngày cuối cùng của tháng, một tùy chọn là chỉ cần phát hiện xem ngày mai có phải là ngày đầu tiên hay không (như một phần của tập lệnh của bạn hoặc trong chính crontab).

Vì vậy, một cái gì đó như:

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

nên là một khởi đầu tốt, giả sử bạn có một datechương trình tương đối thông minh .

Nếu datechương trình của bạn không đủ nâng cao để cung cấp cho bạn những ngày tương đối, bạn có thể chỉ cần tổng hợp một chương trình rất đơn giản để cung cấp cho bạn ngày mai trong tháng (bạn không cần toàn bộ sức mạnh của date), chẳng hạn như:

#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;
}

và sau đó sử dụng (giả sử bạn đã gọi nó tomdomcho "ngày mai trong tháng"):

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

Mặc dù bạn có thể muốn xem xét thêm tính năng kiểm tra lỗi vì cả hai time()mktime()có thể trả lại -1nếu có sự cố. Đoạn mã trên, vì lý do đơn giản, không tính đến điều đó.


7
vâng có thể dễ dàng hơn để chạy vào mỗi ngày đầu tiên thay vì ngày cuối cùng :)
Utku Dalmaz

1
Đúng là ngày đầu tiên của tháng. Đây là cách mã sẽ trông như thế nào trong PHP $ date = new DateTime ('2013-03-01'); $ ngày-> sửa đổi ('- 1 tháng'); $ beforeMonth = $ date-> format ('Y-m'); // $ beforeMonth hiện là 2013-02. Tạo truy vấn để tìm nạp các sản phẩm cho tháng trước.
Lamy

Dữ liệu của năm nhuận feb 29 sẽ bị mất, chúng ta cũng cần xem xét điều đó. Câu trả lời dưới đây của Thunder Rabbit hãy xem xét điều đó nhưng cron đã chạy hai lần trong năm nhuận
Hari Swaminathan

1
@Hari, giải pháp ưu tiên sẽ là chạy vào ngày đầu tiên của tháng và thu thập dữ liệu của tháng trước. Ngày 29 tháng 2 sẽ không thể bỏ qua trong trường hợp đó.
paxdiablo

1
Xem xét các năm nhuận: và nếu bạn không bận tâm, nó sẽ chạy hai lần: 55 23 28,29 2 * myjob.sh
radiorRazor

52

Có một phương pháp ngắn hơn một chút có thể được sử dụng tương tự như một trong những phương pháp ở trên. Đó là:

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

Ngoài ra, mục crontab có thể được cập nhật để chỉ kiểm tra vào ngày 28 đến ngày 31 vì việc chạy nó vào những ngày khác trong tháng là vô nghĩa. Cái nào sẽ cung cấp cho bạn:

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

Tôi không thể làm cho điều này hoạt động như một mục nhập crontab (tôi nghĩ cần phải thoát một cái gì đó). Tuy nhiên, nó hoạt động tốt trong một tập lệnh shell được gọi từ crontab. FYI, lỗi mà tôi mắc phải /bin/sh: -c: line 1: unexpected EOF while looking for matching ')'.
Mark Rajcok

13
Điều này hoạt động tuyệt vời. Trong tệp crontab,% phải được thoát. Vì vậy[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM

Quả thật là một mẹo hay! Nhưng câu hỏi đã được gắn thẻ posixngày POSIX không hỗ trợ "-d +1day": - \ Một giải pháp phức tạp hơn (và xấu xí hơn) sẽ là:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau

18

Thiết lập một công việc cron để chạy vào ngày đầu tiên của tháng. Sau đó, thay đổi đồng hồ của hệ thống để đi trước một ngày.


10
có nghĩa là đồng hồ hệ thống sẽ luôn sai. Xin lỗi nhưng tôi nghĩ điều này sẽ gây ra nhiều vấn đề hơn. -1 sau đó.
Rudy

69
Bây giờ tôi biết Galileo cảm thấy thế nào.
Tom Anderson

7
@iconoclast: Tôi muốn coi nó giống như một công án hơn là một trò đùa.
Tom Anderson

14
Tôi nghĩ rằng đây là một ý tưởng thực sự tồi và tuyệt vời. Đừng làm điều này ở nhà, trẻ em.
Pascal

11
@pascalbetz Ồ, mọi người có thể làm ở nhà, nhưng họ thực sự không nên làm ở nơi làm việc.
Tom Anderson

14

Còn cái này, sau Wikipedia?

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

Vậy thì sao? Đó là: "lỗi ngày trong tháng tồi tệ trong tệp crontab, không thể cài đặt. Bạn có muốn thử lại cùng một chỉnh sửa không?"
webjunkie

12
Chỉ cần rõ ràng, mục nhập wikipedia đó cũng đề cập rằng "L" là không chuẩn.
sdupton

10

Điều chỉnh giải pháp của paxdiablo, tôi chạy vào ngày 28 29 tháng Hai. Dữ liệu từ ngày 29 ghi đè lên ngày 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
Trừ khi, tất nhiên, công việc có một số khía cạnh tiêu cực như giải phóng ra tất cả dữ liệu vì nó được xử lý :-)
paxdiablo

Đây thực sự là một lựa chọn không gây đau đớn (ít kỹ thuật nhất), và bạn có thể không quan tâm rằng 3 năm nữa bạn sẽ nhận được cronjob quá sớm nếu bạn chỉ bỏ qua ,29.
Matt

@Matt: Umm, ý bạn không phải là nó sẽ chạy quá sớm một ngày một lần trong bốn năm nếu mục crontab của bạn cho biết 55   23   28    2?
G-Man nói 'Phục hồi Monica'

@ G-Man Vâng, bạn nói đúng, và trên hết, bạn đã chạy nó lần thứ hai vào ngày 29.
Matt

8

Bạn có thể thiết lập một công việc cron để chạy vào mỗi ngày trong tháng và để nó chạy một tập lệnh shell như sau. Tập lệnh này tính xem số ngày của ngày mai có ít hơn ngày hôm nay hay không (tức là nếu ngày mai là một tháng mới) và sau đó làm bất cứ điều gì bạn muốn.

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

Tôi không chắc bạn thu được gì bằng cách kiểm tra xem nó có ít hơn hôm nay không - bạn chỉ có thể kiểm tra xem đó có phải là ngày đầu tiên hay không. Trừ khi ngày đầu tiên của tháng có thể là ngày 2 hoặc 3 :-)
paxdiablo

7

Để có phương pháp an toàn hơn trong crontab dựa trên giải pháp @Indie (sử dụng đường dẫn tuyệt đối tới date+ $()không hoạt động trên tất cả các hệ thống 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

Để triển khai AWS Cloudwatch cron (Lên lịch Lambdas, v.v.), điều này hoạt động:

55 23 L * ? *

Chạy lúc 11:55 tối vào ngày cuối cùng của mỗi tháng.



3

Bạn chỉ có thể kết nối tất cả các câu trả lời trong một dòng cron và chỉ sử dụng date lệnh.

Chỉ cần kiểm tra sự khác biệt giữa ngày trong tháng là hôm nay và ngày mai:

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

Nếu sự khác biệt này dưới 0 có nghĩa là chúng tôi thay đổi tháng và có ngày cuối cùng của tháng.


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

1
lặp lại lệnh và đưa nó vào bash là cách tốt nhất khi làm việc với cron. Vì cron đang sử dụng sh chứ không phải bash. Kiểm tra vị trí bash của bạn đang sử dụng 'which bash'. Trên FreeBSD là / usr / local / bin / bash, trên Linux / bin / bash.
Donald Duck

2

Cái này thì sao?

chỉnh sửa .bashprofilecách thêm của người dùng :

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

Sau đó, thêm mục nhập này vào crontab:

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

0

Ngày cuối cùng của tháng có thể là 28-31 tùy thuộc vào tháng đó là gì (tháng 2, tháng 3, v.v.). Tuy nhiên, trong một trong hai trường hợp này, ngày hôm sau luôn là ngày đầu tiên của tháng sau. Vì vậy, chúng tôi có thể sử dụng điều đó để đảm bảo chúng tôi luôn chạy một số công việc vào ngày cuối cùng của tháng bằng cách sử dụng mã bên dưới:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.