我在 RedHat 上使用 Bash。我需要安排一个 cron 作业在每个月的第一个星期日上午 9:00 运行。我怎样才能做到这一点?
10 回答
您可以在crontab
文件中添加以下内容:
00 09 * * 7 [ $(date +\%d) -le 07 ] && /run/your/script
为您提供当天的date +%d
编号,然后您可以检查当天是否小于或等于 7。如果是,请运行您的命令。
如果您仅在星期日运行此脚本,则应该意味着它仅在该月的第一个星期日运行。
请记住,在crontab
文件中,命令的格式选项date
应该被转义。
值得注意的是,看起来最明显的解决这个问题的方法是行不通的。
您可能认为您可以只编写一个 crontab 条目,将星期几指定为 0(代表星期日),将月份指定为 1-7,如下所示...
# This does NOT work.
0 9 1-7 * 0 /path/to/your/script
...但是,由于Cron 处理带有指定日期和日期的 crontab 行的古怪方式,这将不起作用,实际上将在 1 日、2 日、3 日运行,每月的 4 号、5 号、6 号和 7 号(无论是星期几)和每月的每个星期日。
这就是为什么您会看到建议使用[ ... ]
检查date
来设置这样的规则 - 在 crontab 中指定星期几并[
在date
运行脚本,如接受的答案所示,或在 crontab 中指定月份范围,并在运行前使用[
anddate
来检查星期几,如下所示:
# This DOES work.
0 9 1-7 * * [ $(date +\%u) = 7 ] && /path/to/your/script
如果您想确保您的 crontab 行无论您在什么操作系统上使用它都能正常工作,请记住一些最佳实践:
- 使用
=
, not==
进行比较。它更便携,因为并非所有 shell 都使用[
支持==
操作符的实现。 - 使用说明
%u
符将date
星期几作为数字而不是%a
运算符获取,因为%a
根据date
运行的语言环境会给出不同的结果。 - 只需使用
date
, not/bin/date
or/usr/bin/date
,因为该date
实用程序在不同系统上有不同的位置。
您需要结合两种方法:
a) 用于cron
每周日上午 9:00 运行作业。
00 09 * * 7 /usr/local/bin/once_a_week
b) 在开始时once_a_week
,通过 shell、Python、C/C++、... 计算日期并提取月份中的日期,并测试 1 到 7 之间的值,包括 1 到 7。如果是,执行真正的脚本;如果没有,请默默退出。
一个 hacky 解决方案:让您的 cron 作业每周日运行,但让您的脚本在开始时检查日期,如果当月的日期大于 7,则立即退出...
这也适用于工作日的名称:
0 0 1-7 * * [ "$(date '+\%a')" == "Sun" ] && /usr/local/bin/urscript.sh
但,
[ "$(date '+\%a')" == "Sun" ] && echo SUNDAY
由于 crontab 中对“%”的特殊处理,将在命令行上失败(也适用于https://stackoverflow.com/a/3242169/2919695)
在第一个星期一,第三个星期二,上个星期日,任何事情上运行一个 cron 任务。
http://xr09.github.io/cron-last-sunday/
只需将run-if-today
脚本放在路径中并与 cron 一起使用即可。
30 6 * * 6 root run-if-today 1 Sat && /root/myfirstsaturdaybackup.sh
如果日期正确,run-if-today
脚本只会返回 0(True 的 bash 值)。
编辑:
现在界面更简单,周数只有一个参数。
# run every first saturday
30 6 * * 6 root run-if-today 1 && /root/myfirstsaturdaybackup.sh
# run every last sunday
30 6 * * 7 root run-if-today L && /root/lastsunday.sh
也许使用 cron.hourly 来调用另一个脚本。然后该脚本将检查它是否是该月的第一个星期日和上午 9 点,如果是,则运行您的程序。对我来说听起来足够优化:-)。
如果您不希望 cron 每天或每个星期天运行您的工作,您可以编写一个包装器来运行您的代码,确定下一个星期天,并安排自己在该日期运行。
然后将该包装器安排在本月的下一个第一个星期日。之后它将自己处理所有事情。
代码将类似于(强调某事......没有进行错误检查):
#! /bin/bash
#We run your code first
/path/to/your/code
#now we find the next day we want to run
nskip=28 #the number of days we want to check into the future
curr_month=`date +"%m"`
new_month=`date --date='$nskip days' +"%m"`
if [[ curr_month = new_month ]]
then
((nskip+=7))
fi
date=`date --date='$nskip days' +"09:00AM %D` #you may need to change the format if you use another scheduler
#schedule the job using "at"
at -m $date < /path/to/wrapper/code
找到下一个第一个星期日的逻辑很简单。由于我们从当月的第一个星期日开始,加上 28 将使我们成为当月的最后一个星期日或下个月的第一个星期日。如果是当前月份,我们会递增到下一个星期日(即下个月的第一周)。
我用了“at”。我不知道这算不算作弊。主要想法是找到下一个第一个星期日。之后您可以替换您想要的任何调度程序,因为您知道要运行作业的日期和时间(不过,不同的调度程序可能需要不同的日期语法)。
00 09 1-7 * 0 /usr/local/bin/once_a_week
每月前 7 天的每个星期日