Lên lịch vào ngày cuối cùng hàng tháng


10

Tôi đọc từ một hướng dẫn để lên lịch cho một kịch bản vào ngày cuối cùng của tháng:

Lưu ý:
Người đọc sắc sảo có thể tự hỏi làm thế nào bạn có thể đặt lệnh để thực thi vào ngày cuối cùng của mỗi tháng vì bạn không thể đặt giá trị ngày trong tháng để trả cho mỗi tháng. Vấn đề này đã làm khổ các lập trình viên Linux và Unix, và đã sinh ra khá nhiều giải pháp khác nhau. Phương pháp phổ biến là thêm câu lệnh if-then sử dụng lệnh date để kiểm tra xem ngày mai là 01:

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

Điều này kiểm tra mỗi ngày vào lúc 12 giờ trưa để xem đó có phải là ngày cuối cùng của tháng không và nếu có, cron sẽ chạy lệnh.

nhập mô tả hình ảnh ở đây

Làm thế nào để [`date +%d -d tomorrow` = 01 ]làm việc?
Là chính xác để nhà nước then; command1?


Bạn có chắc chắn đó là nguyên văn những gì nó nói? Như được viết ở đây, trên thực tế nó không hoạt động.
Michael Homer

Tôi đã đăng ảnh chụp nhanh. @ MichaelHomer
Giải tích

Cảm ơn! Tôi đã thay đổi định dạng để làm cho nó khớp - nó vẫn không hoàn toàn đúng, nhưng đó là những gì hình ảnh nói.
Michael Homer

1
Mất tích ; endif?
danblack

Nó không hoạt động. Nó chứa lỗi cú pháp: Không có khoảng trắng sau [và không có fiở cuối. Ngoài ra, %là đặc biệt trong crontabs.
Kusalananda

Câu trả lời:


17

trừu tượng

Mã chính xác phải là:

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

Gọi tập lệnh này end_of_month.shvà cuộc gọi trong cron chỉ đơn giản là:

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

Điều đó sẽ chạy tập lệnh end_of_month(trong nội bộ sẽ kiểm tra xem ngày đó là ngày cuối cùng của tháng) chỉ vào các ngày 28, 29, 30 và 31. Không cần kiểm tra cuối tháng vào bất kỳ ngày nào khác.

Bài cũ.

Đó là một trích dẫn từ cuốn sách "Dòng lệnh Linux và Kinh thánh Shell Scripting" của Richard Blum, Christine Bresnahan Trang 442, Ấn bản thứ ba, John Wiley & Sons © 2015.

Vâng, đó là những gì nó nói, nhưng đó là sai / không đầy đủ:

  • Thiếu một đóng cửa fi.
  • Cần không gian giữa [và sau đây `.
  • Nó được mạnh mẽ khuyến khích sử dụng $ (...) thay vì `…`.
  • Điều quan trọng là bạn sử dụng các trích dẫn xung quanh việc mở rộng như"$(…)"
  • Có thêm ;sauthen

Làm sao tôi biết? (tốt, theo kinh nghiệm) nhưng bạn có thể thử Shellcheck . Dán mã từ cuốn sách (sau dấu hoa thị) và nó sẽ hiển thị cho bạn các lỗi được liệt kê ở trên cộng với "thiếu shebang". Một tập lệnh không có bất kỳ lỗi nào trong Shellcheck là:

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

Trang web đó hoạt động vì những gì được viết là "mã vỏ". Đó là một cú pháp hoạt động trong nhiều shell.

Một số vấn đề mà shellcheck không đề cập đến là:

  • Giả sử lệnh ngày là phiên bản ngày của GNU. Một với một -dlựa chọn chấp nhận tomorrownhư một giá trị (busybox có một tùy chọn -d nhưng không hiểu ngày mai và BSD có một -dlựa chọn nào khác không liên quan đến "hiển thị" thời gian).

  • Tốt hơn là đặt định dạng sau tất cả các tùy chọn date -d tomorrow +'%d'.

  • Thời gian bắt đầu cron luôn theo giờ địa phương, có thể khiến một công việc bắt đầu sớm hơn 1 giờ so với số ngày chính xác nếu DST (thời gian tiết kiệm ánh sáng ban ngày) được đặt hoặc không được đặt.

Những gì chúng tôi đã thực hiện là một kịch bản shell có thể được gọi bằng cron. Chúng ta có thể sửa đổi thêm tập lệnh để chấp nhận các đối số của chương trình hoặc lệnh để thực thi, như thế này (cuối cùng, mã chính xác):

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

Gọi tập lệnh này end_of_month.shvà cuộc gọi trong cron chỉ đơn giản là:

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

Điều đó sẽ chạy tập lệnh end_of_month(trong nội bộ sẽ kiểm tra xem ngày đó là ngày cuối cùng của tháng) chỉ vào các ngày 28, 29, 30 và 31. Không cần kiểm tra cuối tháng vào bất kỳ ngày nào khác.

Hãy chắc chắn rằng đường dẫn chính xác được bao gồm. PATH bên trong cron sẽ không (không có khả năng) giống với PATH của người dùng.

Lưu ý rằng có một tập lệnh cuối tháng được thử nghiệm (như được chỉ ra bên dưới) có thể gọi nhiều tiện ích hoặc tập lệnh khác.

Điều này cũng sẽ tránh được vấn đề bổ sung mà cron tạo ra với dòng lệnh đầy đủ:

  • Cron chia dòng lệnh trên bất kỳ %ngay cả khi được trích dẫn bằng 'hoặc "(chỉ một \tác phẩm ở đây). Đó là một cách phổ biến trong đó công việc cron thất bại.

Bạn có thể kiểm tra xem end_of_month.shtập lệnh có hoạt động chính xác vào một ngày nào đó không (không phải đợi đến cuối tháng để phát hiện ra tập lệnh không hoạt động) bằng cách kiểm tra tập lệnh đó với faketime:

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

ast-open date(hoặc datenội dung của ksh93 nếu ksh93 được xây dựng như một phần của ast-open) không hỗ trợ date -d tomorrow +%shoặc date +%s tomorrow).
Stéphane Chazelas

2
Kinh nghiệm đã chứng minh (nhiều lần) rằng sẽ tốt hơn rất nhiều khi kiểm tra tập lệnh chạy chính xác với faketime so với việc chờ đến cuối tháng để thấy rằng công việc được lên lịch không hoạt động. Giống như khám phá ra rằng nó * * * * * echo "$(date -u +'date %c')" >>~/testfilesẽ không hoạt động bởi vì người ta quên trích dẫn \%(điều này trở nên khó gỡ lỗi nếu chỉ một lần thử mỗi tháng là khả thi). @Kusalananda
Isaac

8

Giả sử rằng các lỗi cú pháp đã được sửa và lệnh được điều chỉnh lại một chút để ít dài dòng hơn:

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

Điều này chạy date +%d -d tomorrow(giả sử rằng GNU dateđược sử dụng) để lấy ngày mai là số có hai chữ số. Nếu con số không 01, thì hôm nay không phải là ngày cuối cùng của tháng. Trong trường hợp đó, các cuộc thử nghiệm thành công và command1đang không thực thi. Công việc được thực hiện vào buổi trưa vào những ngày có thể là ngày cuối cùng của tháng.

Lệnh ban đầu:

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

Điều này có một vài vấn đề:

  • Không có không gian sau [.
  • A ;trực tiếp sau then.
  • %là đặc biệt trong thông số kỹ thuật công việc cron và phải được thoát như \%(xem man 5 crontab).
  • Không có trận chung fikết nào phù hợp với if.

2
Vấn đề với việc sử dụng [ ... ] && command1thay vì if...là vào những ngày không phải là ngày cuối cùng của tháng, công việc cron sẽ kết thúc với một trạng thái thoát khác không và rằng sự thất bại có thể phải được báo cáo. Sử dụng [ "$(...)" != 01 ] || command1là một cách khác để tránh vấn đề.
Stéphane Chazelas

1
Một vấn đề khác với mã gốc là ;giữa thencommand1.
Stéphane Chazelas

@ StéphaneChazelas Một lý do khác khiến tôi không thích một lớp lót, chúng rất khó đọc.
Kusalananda

Chênh lệch thời gian giữa các lần chạy liên tiếp của lệnh có thể không phải là số nguyên trong (24 giờ) ngày vì thay đổi DST sẽ thay đổi thời gian bắt đầu của cron.
Isaac

3
@cat Không quan trọng bạn trích dẫn như thế nào, "hoặc '(ngoại trừ \), dấu phần trăm %sẽ khiến cron phá vỡ dòng thành hai phần. Đó là một cách thông thường để khiến cron thất bại.
Isaac

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.