งาน CRON จะทำงานในวันสุดท้ายของเดือน


97

ฉันต้องการสร้างงาน CRON ที่จะทำงานในวันสุดท้ายของทุกเดือน ฉันจะสร้างโดยใช้ cPanel

ขอความช่วยเหลือใด ๆ ขอบคุณ

คำตอบ:


187

อาจเป็นวิธีที่ง่ายที่สุดคือการทำงานสามอย่างแยกกัน:

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

ซึ่งจะดำเนินการในวันที่ 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
วันที่ 1 ของเดือนแน่นอน นี่คือลักษณะของโค้ดใน PHP $ date = new DateTime ('2013-03-01'); $ date-> แก้ไข ('- 1 เดือน'); $ previousMonth = $ date-> รูปแบบ ('Y-m'); // $ previousMonth ตอนนี้คือ 2013-02 สร้างแบบสอบถามเพื่อดึงสินค้าสำหรับเดือนก่อนหน้า
Yasen

ข้อมูลของปีอธิกสุรทิน 29 กุมภาพันธ์จะหายไปเราต้องพิจารณาด้วยเช่นกัน คำตอบของ Thunder Rabbit ด้านล่างพิจารณาว่า แต่ cron ทำงานสองครั้งในเดือนกุมภาพันธ์ของปีอธิกสุรทิน
Hari Swaminathan

1
@Hari วิธีแก้ปัญหาที่ต้องการจะทำงานในวันแรกของเดือนและรวบรวมข้อมูลของเดือนก่อนหน้า วันที่ 29 ก.พ. จะพลาดไม่ได้ในกรณีนั้น
paxdiablo

1
พิจารณาปีอธิกสุรทิน: และถ้าคุณไม่คิดว่ามันจะทำงานสองครั้ง: 55 23 28,29 2 * myjob.sh
RadiantRazor

53

มีวิธีที่สั้นกว่าเล็กน้อยซึ่งสามารถใช้ได้คล้ายกับวิธีข้างต้น นั่นคือ:

[ $(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 อย่างไรก็ตาม FYI /bin/sh: -c: line 1: unexpected EOF while looking for matching ')'ข้อผิดพลาดที่ผมได้รับคือ
Mark Rajcok

13
ใช้งานได้ดี ในไฟล์ crontab% ต้องเป็นค่า Escape ดังนั้น[ $(date -d +1day +\%d) -eq 1 ] && run_job
ColinM

เคล็ดลับที่ดีแน่นอน! แต่คำถามถูกแท็กposixและวันที่ POSIXไม่รองรับ"-d +1day": - \ วิธีแก้ปัญหาที่ซับซ้อนกว่า (และน่าเกลียด) จะเป็น:[ `date +\%d` -eq `cal | xargs echo | awk '{print $NF}'` ] && myscript.sh
ckujau

15

ตั้งค่างาน cron เพื่อทำงานในวันแรกของเดือน จากนั้นเปลี่ยนนาฬิกาของระบบเป็นหนึ่งวันข้างหน้า


11
ซึ่งหมายความว่านาฬิกาของระบบจะผิดพลาดตลอดเวลา ขออภัยฉันคิดว่านี่จะทำให้เกิดปัญหามากขึ้น -1 แล้ว
Rudy

71
ตอนนี้ฉันรู้แล้วว่ากาลิเลโอรู้สึกอย่างไร
Tom Anderson

7
@iconoclast: ฉันอยากจะคิดว่ามันเป็นเรื่องของโคอันมากกว่าเรื่องตลก
Tom Anderson

15
ฉันคิดว่านี่เป็นความคิดที่แย่และยอดเยี่ยมจริงๆ อย่าทำที่บ้านนะเด็ก ๆ
Pascal

12
@pascalbetz โอ้คนก็ทำได้ที่บ้าน แต่ไม่ควรทำที่ทำงานจริงๆ
Tom Anderson

14

แล้วสิ่งนี้หลังจาก Wikipedia ล่ะ?

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

แล้วมันเป็นอย่างไร? ว่า: "เกิดข้อผิดพลาดของวันที่ไม่ถูกต้องในไฟล์ crontab ติดตั้งไม่ได้คุณต้องการลองแก้ไขแบบเดิมอีกครั้งหรือไม่"
webjunkie

12
เพื่อให้ชัดเจนว่ารายการวิกิพีเดียยังระบุด้วยว่า "L" ไม่ได้มาตรฐาน
sdupton

10

การปรับแก้โซลูชันของ paxdiablo ฉันทำงานในวันที่ 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

@ Matt: เอ่อคุณไม่ได้หมายความว่ามันจะทำงานวันหนึ่งเร็วเกินไปครั้งเดียวในรอบสี่ปีถ้ารายการ crontab ของคุณบอกว่า55   23   28    2?
G-Man กล่าวว่า 'Reinstate Monica'

@ G-Man ใช่คุณพูดถูกและยิ่งไปกว่านั้นคุณเปิดตัวครั้งที่สองในวันที่ 29
Matt

8

คุณสามารถตั้งค่างาน cron ให้ทำงานได้ทุกวันของเดือนและเรียกใช้เชลล์สคริปต์ดังต่อไปนี้ สคริปต์นี้ระบุว่าจำนวนวันพรุ่งนี้น้อยกว่าวันนี้หรือไม่ (เช่นถ้าพรุ่งนี้เป็นเดือนใหม่) จากนั้นทำสิ่งที่คุณต้องการ

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

ฉันไม่แน่ใจว่าคุณได้อะไรจากการตรวจสอบว่ามันน้อยกว่าวันนี้หรือไม่คุณควรจะตรวจสอบได้ว่าเป็นวันที่ 1 หรือไม่ เว้นแต่วันแรกของเดือนอาจเป็นวันที่ 2 หรือ 3 :-)
paxdiablo

7

สำหรับวิธีที่ปลอดภัยยิ่งขึ้นใน crontab ที่ใช้โซลูชัน @Indie (ใช้พา ธ สัมบูรณ์ไปที่date+ ใช้$()ไม่ได้กับระบบ crontab ทั้งหมด):

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

6

การใช้งาน cron บางอย่างสนับสนุนค่าสถานะ "L" เพื่อแสดงวันสุดท้ายของเดือน

หากคุณโชคดีที่ได้ใช้หนึ่งในการใช้งานเหล่านี้ทำได้ง่ายๆดังนี้:

0 55 23 L * ?

ซึ่งจะเริ่มในเวลา 23:55 น. ของทุกวันสุดท้ายของเดือน

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


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 (การตั้งเวลา Lambdas ฯลฯ .. ) สิ่งนี้ใช้ได้:

55 23 L * ? *

วิ่งเวลา 23:55 น. ของวันสุดท้ายของแต่ละเดือน



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
การสะท้อนคำสั่งและวางลงใน bash เป็นวิธีที่ดีที่สุดเมื่อทำงานกับ cron เนื่องจาก cron ใช้ sh และไม่ทุบตี ตรวจสอบว่า bash ของคุณใช้ 'which bash' อยู่ที่ไหน ใน FreeBSD มันคือ / usr / local / bin / bash บน Linux / bin / bash
Donald Duck

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

1

วันสุดท้ายของเดือนอาจเป็นวันที่ 28-31 ขึ้นอยู่กับเดือนนั้น (กุมภาพันธ์มีนาคมเป็นต้น) อย่างไรก็ตามในกรณีนี้วันถัดไปคือวันที่ 1 ของเดือนถัดไปเสมอ ดังนั้นเราสามารถใช้สิ่งนั้นเพื่อให้แน่ใจว่าเราทำงานบางอย่างเสมอในวันสุดท้ายของเดือนโดยใช้รหัสด้านล่าง:

0 8 28-31 * * [ "$(date +%d -d tomorrow)" = "01" ] && /your/script.sh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.