安排每月的最后一天


10

我从一条指令中了解到如何在一个月的最后一天安排脚本:

注意:
精明的读者可能会想知道如何设置命令才能在每月的最后一天执行,因为您无法将dayofmonth值设置为覆盖每个月。这个问题困扰着Linux和Unix程序员,并催生了许多不同的解决方案。一种常见的方法是添加一个if-then语句,该语句使用date命令检查明天的日期是否为01:

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

该命令每天中午12点进行检查,以查看是否是该月的最后一天,如果是,则cron运行命令。

在此处输入图片说明

[`date +%d -d tomorrow` = 01 ]工作如何?
陈述正确then; command1吗?


您确定这是一字不漏吗?就像这里写的那样,实际上行不通的。
Michael Homer

我发布了快照。@ MichaelHomer
微积分

谢谢!我一直在摆弄要使其匹配的格式-仍然不太正确,但这就是图片所说的。
迈克尔·荷马

1
失踪了; endif吗?
danblack

没用 它包含语法错误:没有足够的空间后[,并没有fi底。另外,%在crontabs中是特殊的。
库萨兰达

Answers:


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天运行脚本(内部会检查日期是否是该月的最后一天)。无需检查其他任何一天的月末。

旧帖子。

这是引自Richard Blum的书“ Linux Command Line and Shell Scripting Bible”,Christine Bresnahan pp 442,第三版,John Wiley&Sons©2015。

是的,这就是它的意思,但这是错误的/不完整的:

  • 错过了闭幕fi
  • [和之间需要空格`
  • 这是强烈建议使用$(...)代替`…`
  • 您使用引号包围是非常重要的扩张"$(…)"
  • 还有一个额外的;then

我怎么知道?(嗯,根据经验☺),但是您可以尝试Shellcheck。从书中粘贴代码(在星号之后),它将向您显示上面列出的错误以及“缺少提示”。Shellcheck中没有任何错误的脚本是这样的:

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

该站点之所以有效,是因为所写的是“外壳代码”。那是在许多shell中都可以使用的语法。

shellcheck没有提到的一些问题是:

  • 假定date命令是GNU日期版本。具有-d选项的选项可以接受tomorrow作为值(busybox具有-d选项,但明天不了解,而BSD具有-d选项,但与时间的“显示”无关)。

  • 最好所有选项之后都设置格式date -d tomorrow +'%d'

  • Cron开始时间始终以本地时间为准,如果已设置或未设置DST(夏令时),则可能会使一项作业比确切的日期晚1小时开始。

我们要做的是一个可以用cron调用的shell脚本。我们可以进一步修改脚本以接受要执行的程序或命令的参数,如下所示(最后是正确的代码):

#!/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天运行脚本(内部会检查日期是否是该月的最后一天)。无需检查其他任何一天的月末。

确保包括正确的路径。cron中的PATH不会(不太可能)与用户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 datedate如果ksh93是ast-open的一部分,则为ksh93 的内置函数)确实支持date -d tomorrow +%sdate +%s tomorrow
斯特凡Chazelas

2
经验已经证明(很多次),用falsetime正确运行脚本要好得多,而要等到月底才能发现计划的工作不起作用。就像发现那* * * * * echo "$(date -u +'date %c')" >>~/testfile行不通,因为一个人忘了引用它\%(如果每月只能尝试一次,则很难调试)。@Kusalananda
以撒

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)。
  • fi末尾没有与匹配的决赛if

2
使用[ ... ] && command1而不是使用的问题if...是,在不是该月最后一天的日期,cron作业将以非零退出状态结束,并且可能必须报告失败。使用[ "$(...)" != 01 ] || command1是避免问题的另一种方法。
斯特凡Chazelas

1
原始代码的另一个问题是;介于then和之间command1
斯特凡Chazelas

@StéphaneChazelas我不喜欢单线纸的另一个原因是,它们很难阅读。
库萨兰达

命令的连续运行之间的时间差可能不是(24小时)天的整数,因为DST更改将改变cron的开始时间。
艾萨克(Isaac)

3
@cat不管引用如何,无论是"还是'(除外\),百分号%都会使cron将该行分为两部分。这是使cron失败的一种常用方法。
艾萨克(Isaac)

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.