กำหนดวันสุดท้ายของทุกเดือน


10

ฉันอ่านจากคำแนะนำในการกำหนดตารางสคริปต์ในวันสุดท้ายของเดือน:

หมายเหตุ:
ผู้อ่านที่ฉลาดอาจสงสัยว่าคุณจะสามารถตั้งคำสั่งให้ดำเนินการในวันสุดท้ายของทุกเดือนได้อย่างไรเพราะคุณไม่สามารถกำหนดค่าวันที่ให้ครอบคลุมทุกเดือนได้ ปัญหานี้ทำให้เกิดโปรแกรมเมอร์ Linux และ Unix และวางไข่โซลูชั่นที่แตกต่างกันไม่กี่ วิธีการทั่วไปคือการเพิ่มคำสั่ง if-then ที่ใช้คำสั่ง date เพื่อตรวจสอบว่าวันในวันพรุ่งนี้คือ 01:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command1

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

ป้อนคำอธิบายรูปภาพที่นี่

วิธีการ[`date +%d -d tomorrow` = 01 ]ทำงานหรือไม่
แก้ไขให้ถูกต้องthen; command1หรือไม่?


คุณแน่ใจหรือว่าใช้คำต่อคำว่าอะไร? ตามที่เขียนไว้จริง ๆ แล้วมันใช้งานไม่ได้
Michael Homer

ฉันโพสต์ภาพรวม @ MichaelHomer
แคลคูลัส

ขอบคุณ! ฉันเล่นการจัดรูปแบบเพื่อให้ตรงกับ - มันยังไม่ถูกต้อง แต่มันเป็นสิ่งที่ภาพพูด
Michael Homer

1
ที่ขาดหายไป; endif?
danblack

มันไม่ทำงาน มันมีข้อผิดพลาดทางไวยากรณ์: ไม่มีช่องว่างหลังจาก[และไม่มีfiที่สิ้นสุด นอกจากนี้ยัง%มีความพิเศษใน crontabs
Kusalananda

คำตอบ:


17

นามธรรม

รหัสที่ถูกต้องควรเป็น:

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

เรียกสคริปต์นี้end_of_month.shและการเรียกเป็น cron เป็นเพียง:

00 12 28-31 * * /path/to/script/end_of_month.sh command

ที่จะเรียกใช้สคริปต์end_of_month(ซึ่งภายในจะตรวจสอบว่าวันนี้เป็นวันสุดท้ายของเดือน) เฉพาะในวันที่ 28, 29, 30 และ 31 ไม่จำเป็นต้องตรวจสอบปลายเดือนในวันอื่น ๆ

โพสต์เก่า

นั่นคือคำพูดจากหนังสือ "Linux Command Line และ Shell Scripting Bible" โดย Richard Blum, Christine Bresnahan pp 442, รุ่นที่สาม, John Wiley & Sons © 2015

ใช่นั่นคือสิ่งที่มันพูด แต่นั่นผิด / ไม่สมบูรณ์:

  • fiที่ขาดหายไปปิด
  • ต้องการพื้นที่ระหว่างและต่อไปนี้[`
  • มันเป็นเรื่องที่ขอแนะนำให้ใช้ $ ( ... ) `…`แทน
  • มันเป็นสิ่งสำคัญที่คุณจะต้องใช้เครื่องหมายคำพูดรอบการขยายเช่น"$(…)"
  • มีเพิ่มเติม;หลังจากthen

ฉันจะรู้ได้อย่างไร (โดยประสบการณ์☺) แต่คุณสามารถลองใช้Shellcheckได้ วางรหัสจากหนังสือ (หลังเครื่องหมายดอกจัน) แล้วมันจะแสดงข้อผิดพลาดตามรายการข้างต้นพร้อมกับ "shebang ที่หายไป" สคริปต์ที่ไม่มีข้อผิดพลาดใน Shellcheck คือ:

#!/bin/sh if [ "$(date +%d -d tomorrow)" = 01 ] ; then script.sh; fi

ไซต์นั้นใช้งานได้เพราะสิ่งที่เขียนคือ "รหัสเชลล์" นั่นคือไวยากรณ์ที่ใช้งานได้ในหลาย ๆ เชลล์

ปัญหาบางอย่างที่ shellcheck ไม่ได้กล่าวถึงคือ:

  • มันสันนิษฐานว่าคำสั่ง date เป็นเวอร์ชั่นของ GNU หนึ่งที่มี-dตัวเลือกที่ยอมรับtomorrowเป็นค่า (busybox มีตัวเลือก -d แต่ไม่เข้าใจในวันพรุ่งนี้และ BSD มี-dตัวเลือก แต่ไม่เกี่ยวข้องกับ "แสดง" เวลา)

  • มันเป็นการดีกว่าที่จะกำหนดรูปแบบหลังจากตัวเลือกdate -d tomorrow +'%d'ทั้งหมด

  • เวลาเริ่มต้น cron อยู่ในเวลาท้องถิ่นเสมอซึ่งอาจทำให้หนึ่งงานเริ่มต้นก่อนหน้า 1 ชั่วโมงช้ากว่าการนับวันที่แน่นอนถ้า DST (การปรับเวลาตามฤดูกาล) ถูกตั้งค่าหรือยกเลิกการตั้งค่า

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

#!/bin/sh
[ "$#" -eq 0 ] && echo "Usage: $0 command [args]" && exit 1
[ "$(date -d tomorrow +'%d')" = 01 ] || exit 0
exec "$@"

เรียกสคริปต์นี้end_of_month.shและการเรียกเป็น cron เป็นเพียง:

00 12 28-31 * * /path/to/script/end_of_month.sh command

ที่จะเรียกใช้สคริปต์end_of_month(ซึ่งภายในจะตรวจสอบว่าวันนี้เป็นวันสุดท้ายของเดือน) เฉพาะในวันที่ 28, 29, 30 และ 31 ไม่จำเป็นต้องตรวจสอบปลายเดือนในวันอื่น ๆ

ตรวจสอบให้แน่ใจว่ามีเส้นทางที่ถูกต้อง PATH ภายใน cron จะไม่ (ไม่น่าจะเป็น) เหมือนกับ PATH ของผู้ใช้

ทราบว่ามีหนึ่งในตอนท้ายของเดือนสคริปต์การทดสอบ (ตามที่ระบุไว้ด้านล่าง) ที่สามารถเรียกสาธารณูปโภคอื่น ๆ อีกมากมายหรือสคริปต์

นี่จะหลีกเลี่ยงปัญหาเพิ่มเติมที่ cron สร้างด้วยบรรทัดคำสั่งแบบเต็ม:

  • Cron แยกบรรทัดคำสั่งใด ๆ%แม้ว่าจะยกมาด้วย'หรือ"(เฉพาะ\งานที่นี่) นั่นเป็นวิธีการทั่วไปที่งาน cron ล้มเหลว

คุณสามารถทดสอบว่าend_of_month.shสคริปต์ทำงานอย่างถูกต้องในบางวันหรือไม่ (โดยไม่ต้องรอจนถึงสิ้นเดือนเพื่อตรวจสอบว่าสคริปต์ไม่ทำงาน) โดยการทดสอบด้วย faketime:

$ faketime 2018/10/31 ./end_of_month echo "Command will be executed...."
Command will be executed....

ast-open date(หรือdatebuiltin ของ ksh93 หาก ksh93 ถูกสร้างขึ้นเป็นส่วนหนึ่งของ ast-open) รองรับdate -d tomorrow +%sหรือdate +%s tomorrow)
Stéphane Chazelas

2
ประสบการณ์ได้พิสูจน์แล้ว (หลาย ๆ ครั้ง) ว่าเป็นการดีกว่ามากในการทดสอบสคริปต์ที่ทำงานอย่างถูกต้องด้วย faketime กว่าจะรอจนถึงสิ้นเดือนเพื่อค้นหาว่างานที่กำหนดไว้ไม่ทำงาน เช่นเดียวกับการค้นพบว่า* * * * * echo "$(date -u +'date %c')" >>~/testfileจะไม่ทำงานเพราะมีคนลืมอ้างถึง\%(ซึ่งยากต่อการตรวจแก้จุดบกพร่องถ้าลองเพียงครั้งเดียวทุกเดือนก็เป็นไปได้) @Kusalananda
Isaac

8

สมมติว่าข้อผิดพลาดทางไวยากรณ์ได้รับการแก้ไขและคำสั่งจะจัดระเบียบใหม่เล็กน้อยเพื่อให้รายละเอียดน้อยลง:

00 12 28-31 * * [ "$( date -d tomorrow +\%d )" != "01" ] || command1

สิ่งนี้จะทำงานdate +%d -d tomorrow(สมมติว่าเป็น GNU dateที่ใช้) เพื่อรับวันพรุ่งนี้เป็นตัวเลขสองหลัก ถ้าตัวเลขไม่ได้01แล้วในวันนี้ไม่ได้เป็นวันสุดท้ายของเดือน ในกรณีที่การทดสอบประสบความสำเร็จและcommand1เป็นที่ไม่ได้ดำเนินการ งานรันตอนเที่ยงในวันที่อาจเป็นวันสุดท้ายของเดือน

คำสั่งเดิม:

00 12 * * * if [`date +%d -d tomorrow` = 01 ] ; then ; command1

มีปัญหาเล็กน้อย:

  • [ไม่มีช่องว่างหลัง
  • โดยตรงหลังจาก;then
  • %เป็นคุณสมบัติพิเศษในงาน cron และจะต้องหลบหนีในฐานะ\%(ดูman 5 crontab)
  • ไม่มีครั้งสุดท้ายในตอนท้ายที่ตรงกับfiif

2
ปัญหาเกี่ยวกับการใช้งาน[ ... ] && command1แทนif...คือในวันที่ไม่ใช่วันสุดท้ายของเดือนงาน cron จะจบลงด้วยสถานะทางออกที่ไม่เป็นศูนย์และอาจต้องรายงานความล้มเหลวนั้น การใช้[ "$(...)" != 01 ] || command1เป็นอีกวิธีหนึ่งในการหลีกเลี่ยงปัญหา
Stéphane Chazelas

1
ปัญหาเกี่ยวกับรหัสเดิมอีกประการหนึ่งคือ;ระหว่างและthen command1
Stéphane Chazelas

@ StéphaneChazelasอีกเหตุผลหนึ่งที่ฉันไม่ชอบผู้เดินสมุทรพวกเขาอ่านยาก
Kusalananda

ความแตกต่างของเวลาระหว่างการรันคำสั่งติดต่อกันอาจไม่ใช่จำนวนเต็ม (24 ชั่วโมง) วันเนื่องจากการเปลี่ยนแปลงเวลาจะเปลี่ยนเวลาเริ่มต้นของ cron
ไอแซค

3
@cat มันไม่สำคัญว่าคุณจะพูดอย่างไร"หรือ'(ยกเว้น\) เครื่องหมายเปอร์เซ็นต์%จะทำให้ cron แตกบรรทัดเป็นสองส่วน นั่นเป็นวิธีปกติในการทำให้ cron ล้มเหลว
ไอแซค

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.