如何指示 cron 每两周执行一次作业?
- 2024-10-11 08:36:00
- admin 原创
- 90
问题描述:
我想通过 cron 运行一个作业,该作业将在每月第二个星期二的给定时间执行。每个星期二的执行很简单:
0 6 * * Tue
但是如何实现“每隔一个星期二”(或者如果你愿意 - 每隔一周)呢?我不想在脚本本身中实现任何逻辑,而只在 cron 中保留定义。
解决方案 1:
回答
修改您星期二的 cron 逻辑,使其从纪元开始每隔一周执行一次。
知道一周有 604800 秒(忽略夏令时变化和闰秒,谢谢),并使用 GNU 日期:
0 6 * * Tue expr `date +%s` / 604800 % 2 >/dev/null || /scripts/fortnightly.sh
除此以外
日历算术令人沮丧。
@xahtep 的回答很棒,但正如@Doppelganger 在评论中指出的那样,它会在某些年份边界上失败。date
实用程序的“年份周数”说明符在这里都无济于事。一月初的某个星期二将不可避免地重复前一年最后一个星期二的周奇偶校验:2016-01-05(%V)、2018-01-02(%U)和 2019-01-01(%W)。
解决方案 2:
crontab
这样怎么样,即使前五个字段没有明确定义,它也会保留它:
0 6 * * Tue expr `date +%W` % 2 > /dev/null || /scripts/fortnightly.sh
解决方案 3:
类似于
0 0 1-7,15-21 * 2
将会在每月的第一和第三个星期二发生。
注意:不要将其与 vixie cron(包含在 RedHat 和 SLES 发行版中)一起使用,因为它会在月份和星期几字段之间生成或,而不是与。
解决方案 4:
可能有点傻,但人们也可以创建两个 cronjob,每个第一个星期二执行一个,每第三个星期二执行一个。
第一个 cronjob:
0 0 8 ? 1/1 TUE#1 *
第二个 cronjob:
0 0 8 ? 1/1 TUE#3 *
不确定这里的语法,我使用http://www.cronmaker.com/来制作这些。
解决方案 5:
pilcrow 的回答很棒。但是,它导致 fortnightly.sh 脚本每双周运行一次(自纪元以来)。如果您需要脚本在奇数周运行,您可以稍微调整他的答案:
0 6 * * Tue expr ( `date +%s` / 604800 + 1 ) % 2 > /dev/null || /scripts/fortnightly.sh
将 1 改为 0 将使其移回偶数周。
解决方案 6:
如果您想根据给定的开始日期执行此操作:
0 6 * * 1 expr ( `date +%s` / 86400 - `date --date='2018-03-19' +%s` / 86400 ) % 14 == 0 > /dev/null && /scripts/fortnightly.sh
从 2018-03-19 开始每隔一个星期一触发
表达式为:如果...则于星期一早上 6 点运行。
1 - 获取今天的日期(以秒为单位),除以一天的秒数,以转换为自纪元以来的天数
2 - 对开始日期执行相同操作,将其转换为自纪元以来的天数
3 - 了解两者之间的区别
4 - 除以 14 并检查余数
5- 如果余数为零,则表示您处于两周的周期
解决方案 7:
我发现上述方法还有一些其他限制,在某些极端情况下可能会失败。例如,考虑:
@xahtep 和 @Doppelganger 讨论了
%W
上述使用某些年份界限的问题。@pilcrow 的答案在某种程度上解决了这个问题,但它也会在某些边界上失败。本主题或其他相关主题中的答案使用一天中的秒数(而不是一周),由于同样的原因,它们也会在某些边界上失败。
这是因为这些方法依赖于 UTC 时间(日期 +%s)。考虑一下我们每月第二个星期二凌晨 1 点和晚上 10 点运行作业的情况。
假设GMT-2:
当地时间凌晨 1 点 =昨天晚上 11 点(UTC)
当地时间晚上 10 点 = UTC今天晚上 8 点
如果我们每天只检查一次,这将不是问题,但如果我们检查多次 - 或者如果我们接近 UTC 时间并且发生夏令时,脚本就不会将这些视为同一天。
为了解决这个问题,我们需要根据我们当地的时区而不是 UTC 来计算与 UTC 的偏移量。我在 BASH 中找不到简单的方法来做到这一点,所以我开发了一个解决方案,使用 Perl 中的快速一行来计算与 UTC 的偏移量(以秒为单位)。
该脚本利用 date +%z 输出本地时区。
Bash 脚本:
TZ_OFFSET=$( date +%z | perl -ne '$_ =~ /([+-])(d{2})(d{2})/; print eval($1."60**2") * ($2 + $3/60);' )
DAY_PARITY=$(( ( `date +%s` + ${TZ_OFFSET} ) / 86400 % 2 ))
然后,确定这一天是奇数还是偶数:
if [ ${DAY_PARITY} -eq 1 ]; then
...
else
...
fi
解决方案 8:
这里有很多不错的答案。根据评论,我看到了相当多的困惑和沮丧。我给出这个答案的目的不仅是回答 OP 的问题如何指示 cron 每两周执行一次作业?,而且还为将来可能阅读此内容的人消除一些困惑。
TL;DR:
crontab 条目如下所示:
<minute> <hour> * * <Day of Week> expr \( $( date+\%s ) \/ 604800 \% 2 \) > /dev/null && <command to run on odd weeks> || <command to run on even weeks>
Crontab 条目:根据手册页,
所有 [用 ] 编写的 crontab 条目都具有以下格式:
1:要执行 [命令] 的小时中的分钟
范围:0-59
2:要执行 [命令] 的一天中的小时
范围:0-23 3:要执行 [命令] 的月份中的日期
范围:1-31
4:要执行 [命令] 的月份
范围:1-12
5:要执行 [命令] 的星期几
范围:检查手册页,可能是 0-6、1-7 或 0-7
6:要执行的命令
注意,还有 @ 值,在这里我就不讨论了。crontab -e
*nix 的某些版本允许表达式:
*/2
= 范围内的每个偶数
1 + */2
= 范围内的每个奇数
1,15
= 第一和第十五
2-8
= 第二到第八(含)
有些版本还允许使用人类可读的单词,例如二月或星期三。
对于月份,*/2 将在二月、四月、六月、八月、十月、十二月执行。
对于星期几,*/2 将在每双日运行 - 请查看手册页以了解星期几是如何编号的。Tue/2 不是有效的表达式。
人类可读性(日历)的困境
你需要知道一些常量:一年
有 365.2464天(为了方便,我们将其四舍五入为 365)。一年
有 12 个月。一周
有 7天。一天
有 86,400秒。
因此,
1 个月为 4-1/3 周
(即 3 个月为 13 周),
1 年为 52-1/7 周
日历数学的祸根:
半月 = 每半月 = 2x/月 = 每年 24 次。
双周 = 每隔一周(每两周)= 每年 26 次。
注意:这些术语经常被误用。
有些年份有 51 周,有些有 53 周,大多数有 52 周。如果我的 cron 每奇数周运行一次(date +%W
模 2),并且一年有 51 或 53 周,它也会在下一周运行,即新年的第一周。相反,如果我的 cron 每偶数周运行一次,它将跳过 2 周。这不是我想要的。
CRON 可以支持半月,但不支持双周!半月:
每月的第一天总是在 1 号和 7 号之间。下半周总是在 15 号和 21 号之间。半月
有两个值,一个在上半月,另一个在下半月。例如:
2,16
Unix 时间
从非常高的层次来看,*nix 时间有 2 个值:
date +%s
= 自纪元 (01/01/1970 00:00:00) 以来的
date +%N
秒数 = 秒的小数部分(以纳秒为单位)
因此,*nix 中的时间是date +%s.%N
*Nix 使用纪元时间。/etc/shadow 文件包含上次更改密码的日期。它是 %s 除以 86,400 的整数部分。
Factino
GPS 卫星测量时间的方式是“自纪元时间以来的周数”和“一周内的(小数)秒数”。
注意:纪元周与年份无关。年份有 51 周、52 周还是 53 周并不重要。纪元周永不滚动。
双周时间算法
在 Nix 日期中 +%W 是一年中的周数,而不是纪元周。 nix 没有纪元周值,但可以计算出来。
纪元周 = 整数(纪元秒 / 每天秒数 / 每周天数)
每两周 = 纪元周模数 2
每两周的值始终为 0 或 1,因此,当 Fortnightly = 0 时运行命令会在每个偶数周运行,而 1 = 每个奇数周运行。
每两周计算)
第一种方式(bc):
date +"%s / 604800 % 2" | bc
这将生成“[Epoch Seconds] / 604800 % 2”
然后,该答案被发送到 bc,这是一个基本的计算器,它会进行计算并将答案回显到 STDOUT,并将任何错误回显到 STDERR。
返回代码为 0。
第二种方法(expr):
在这种情况下,将表达式发送给 expr 并让其进行计算expr $( date +%s ) / 604800 % 2
expr 命令进行计算,将答案回显到 STDOUT,将错误回显到 STDERR。如果答案为 0,则返回代码为 1,否则,返回代码为 0。
通过这种方式,我不关心答案是什么,只关心返回代码。考虑一下:
$ expr 1 % 2 && echo Odd || echo Even
1
Odd
$ expr 2 % 2 && echo Odd || echo Even
0
Even
由于结果无关紧要,我可以将 STDOUT 重定向到 /dev/null。在这种情况下,我将保留 STDERR,以防万一发生意外情况,我会看到错误消息。
Crontab 条目
在 crontab 中,百分号和括号具有特殊含义,因此需要用反斜杠 () 进行转义。
首先,我需要决定是在偶数周还是奇数周运行。或者,也许在奇数周运行 Command_O,在偶数周运行 command_E。然后,我需要转义所有特殊字符。
由于 expr 仅评估 WEEKS,因此需要指定要评估的特定星期几(和时间),例如每周二下午 3:15。因此,我的 crontab 条目的前 5 个文件(时间条目)将是:
15 3 * * TUE
我的 cron 命令的第一部分将是 expr 命令,STDOUT 发送到 null:
expr \( $( date+\%s ) \/ 604800 \% 2 \) > /dev/null
第二部分将是评估器,&&
或者||
第三部分将是我想要运行的命令(或脚本)。如下所示:
expr \( $( date+\%s ) \/ 604800 \% 2 \) > /dev/null && <command_O> || <command_E>
希望这能澄清一些困惑
解决方案 9:
尝试一下
0 0 1-7,15-21 * 2
该活动每月举办两次,每次都在星期二,且间隔至少一周。
解决方案 10:
30 4 /2 1
如果是周一,则每月第二天早上 4:30
解决方案 11:
如果您只想要第二周的每个星期二:
00 06 2#2
解决方案 12:
Cron 提供了“每隔一次”语法“/2”。只需在相应的时间单位字段后加上“/2”,它就会“每隔一次”执行 cronjob。对于您的情况...
0 6 * * Tue/2
上述操作应每隔一个星期二执行一次。
解决方案 13:
语法“/2”与工作日不符。因此,我对上面的智能补充是简单地在 MonthDay 字段上使用 1,15。
0 6 1,15 /scripts/fornightly.sh > /dev/null 2>&1
解决方案 14:
为什么不像
0 0 1-7,15-21,29-31 * 5
这样合适吗?
解决方案 15:
0 0 /14 *
每月 14 日 00:00 运行
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件