Crontab实现从指定时间开始每隔多少秒(分钟/天)执行一次脚本的方法
背景
大家好,最近发生了很多事,已经很久没有在CSDN上推出新文章了。总之呢,我现在是一名大数据开发工程师,会从今天开始不定期地分享自己学到的新技术。如果你对大数据感兴趣的话,欢迎关注我。
最近接到了上级分配的一个任务,希望我编写一个linux的定时任务脚本,能够实现从当前时间开始每隔多少天执行一次脚本。这篇文章的故事就这么开始了~
Crontab的格式
基于这个问题,我开始百度查询关于crontab的知识,我重点讲讲我发现的crontab的局限性,在阐述我的发现之前,先让我们学习一下crontab的格式:
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
- minute: 区间为 0 – 59
- hour: 区间为0 – 23
- day-of-month: 区间为0 – 31
- month: 区间为1 – 12. 1 是1月. 12是12月.
- Day-of-week: 区间为0 – 7. 周日可以是0或7.
Crontab可以说是一款非常优秀的定时启动任务工具,可以让我们指定每隔多少分钟、多少小时、多少天、几个月或者每个星期的星期几来执行任务。
Crontab的使用与局限性
再优秀的工具都有其局限性,在我使用Crontab的过程当中,我发现在指定不同的时间间隔时,Crontab都有其局限性:
- minute: 只可以以一个小时为范围来指定间隔多少分钟。
- hour: 只可以以一天为范围来指定间隔多少小时。
- day-of-month: 只可以以一个月为范围指定间隔多少天。
- month: 只可以以一年为范围指定间隔几个月。
我上面的阐述可能不太易于理解,没关系,让我们结合具体实例来讲解:
- 每隔7分钟运行一次命令:
*/7 * * * * /root/bin/check-status.sh
比如说我在今天的10:05分开启了这个定时任务,我的本意是希望这个定时任务是从当前时间开始每隔7分钟执行。但是事实上不是的,crontab的分钟任务是以一个小时为单位和范围的,也就是该任务会在10:07分第一次执行,并每隔7分钟执行一次,待10:56分的这一次执行完成后,下一次并不会在11:03分执行,而是会每隔一小时重新刷新计时,所以下一次会在11:00分执行,再在11:07分执行,以此反复。这就会导致在两个小时的交界处任务的频繁执行,同时,如果我希望任务每隔61分钟执行一次,目前的crontab也无法很好解决。 - 每隔10小时运行一次命令:
0 */10 * * * /root/bin/check-status.sh
比如说我在今天的9:00 am开启了这个定时任务,我的本意是希望这个定时任务是从当前时间开始每隔10小时执行。但事实上不是的,crontab的小时任务是以一天为单位和范围的,也就是该任务会在10:00 am第一次执行,并每隔10小时执行一次。待20:00pm的这一次执行完成后,由于在等待下一次的执行的过程中,时间来到了两天的交界处,因此定时任务会被重新刷新。也就是下一次任务会在第二天的00:00 am时执行,然后再在10:00am执行,以此反复。这就会导致在两天的交界处任务的频繁执行,同时,如果我希望任务每隔25小时执行一次,目前的crontab也无法很好解决。 - 每隔8天运行一次命令:
0 0 */8 * * /root/bin/check-status.sh
比如说我在1月的6日开启了这个定时任务,我的本意是希望这个定时任务是从今日开始每隔8日执行。但事实上不是的,crontab的天任务是以一个月为单位和范围的,同时又由于每个月的起始值是1日,也就是该任务会在9日的零时零分第一次执行,然后在17日、25日的零时零分分别执行一次。再之后就到了两个月的交界处,该定时任务会被重新刷新,也就是下一次任务会在2月1日的零时零分执行,以此反复。这就会导致在两个月的交界处任务的频繁执行,同时,如果我希望任务每隔32天执行一次,目前的crontab也无法很好解决。 - 每隔5个月执行一次命令:
0 0 1 */5 * /root/bin/check-status.sh
比如说我在2020年4月1日开启了这个定时任务,我的本意是希望这个定时任务从本月开始每隔5个月执行。但事实上不是的,crontab的月任务是以一年为单位和范围的,同时又由于每年的起始值是1月,也就是该任务会在6月1日的零时零分第一次执行,然后在11月1日的零时零分执行一次。再之后就到了两年的交界处,该定时任务会被重新刷新,也就是下一次任务会在2021年的1月1日的零时零分执行,以此反复。这就会导致在两年的交界处任务的频繁执行,同时,如果我希望任务每隔13个月执行一次,目前的crontab也无法很好解决。
解决方法
基于以上crontab存在的局限性和我的需求(需指定从当前时刻开始每隔多少时间执行一次任务),我摸索了一阵子之后,得到了一个不太华丽的解决办法,但是可以帮助广大网友们在时间短、任务重的情形之下应急使用。
解决办法同样是在/etc/cron.d/下编写一个定时启动脚本(crontab方面的基础网上都有,请码友们自行补充学习),只是我们的代码略为复杂。脚本内容如下:
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=""
lastRunTime=1611803325
seconds=120
*/1 * * * * root source /etc/profile && intervalRunTime=$[ (`date +\%s` - $lastRunTime) \% $seconds ] && if [( $intervalRunTime -ge 0 ]] && [[ $intervalRunTime -lt 60 ]];then echo $intervalRunTime>>$HOME/crontab.log; fi
通过观察以上我编写的代码,大家可以发现我是通过将当前时间绝对秒数作为初始声明变量赋予lastRunTime变量,然后再将间隔时间的秒数(若是间隔2分钟就是120秒,间隔1小时就是3600秒)作为初始声明变量赋予seconds变量。
之后,每隔一分钟定时任务脚本就会判断intervalRunTime(当前时间绝对秒数-lastRunTime)是否大于等于0并且小于60,若满足条件,则echo
i
n
t
e
r
v
a
l
R
u
n
T
i
m
e
>
>
intervalRunTime>>
intervalRunTime>>HOME/crontab.log(码友们可以将这部分修改为你们自己需要执行的任务)。
之所以判断条件是大于等于0并且小于60,是因为crontab的最小间隔时间为1分钟,因此将定时任务在每隔seconds后的那一分钟内执行一次。
我的解决办法仍不够优雅,希望码友们如果想到了更好的解决办法或者有什么疑问的话,欢迎在评论区讨论~