cron 是linux下实现任务调度(定时任务)的一种服务,可以在无需人工干预的情况下运行作业。
crond 则是 cron 服务的守护进程,与windows下的计划任务类似。
Linux系统会默认安装cron服务工具,并自动启动crond进程。
crontab 是Linux系统提供的用于设置定时任务的命令行工具。
crontab 也指 cron 服务的配置文件,是“cron table”的缩写,语义上即为“任务调度列表”。
Linux的任务调度主要分为两类:
系统任务调度:系统周期性所要执行的工作,比如系统数据备份、临时文件清理、缓存清理等。
用户任务调度:某个用户定期要执行的工作,比如用户数据备份、定时邮件提醒等,这些工作可由每个用户自行设置。
默认情况下,只有root可以使用crontab,而普通用户不可以,会出现如下提示信息:
root可以通过/etc/cron.allow与/etc/cron.deny两个文件来控制哪个用户有权使用crontab。这两个文件的内容与格式很简单,将需要配置的用户名写入文件,每个用户名占一行。
权限规则如下:
|
cron.allow |
cron.deny |
有权使用crontab的用户范围 |
1 |
文件不存在 |
文件不存在 |
仅root可使用crontab |
2 |
文件存在 |
文件不存在 |
仅cron.allow中的用户可使用crontab |
3 |
文件不存在 |
文件存在 |
只要不在cron.deny中的用户都可以使用crontab |
4 |
文件存在 |
文件存在 |
仅cron.allow中的用户可使用crontab(cron.allow优先级高于cron.deny,此时cron.deny不起作用) |
由于cron.allow与cron.deny同时存在的情况下,cron.deny没有实际意义。因此从逻辑、语义清晰的角度出发,不建议同时保留两个文件。根据实际需求,白名单与黑名单保留一个就好。
系统任务调度对应的配置文件有/etc/crontab文件和/etc/cron.d/目录下的一系列文件。
用户任务调度对应的配置文件在/var/spool/cron/目录下。
cron服务每隔1分钟会去读取这些文件中的调度任务。
这一个小节我们先从宏观上认识这些配置文件,而这些文件的详细配置语法我们留到下一个小节讨论。
/etc/crontab是一个文件,root可以通过这个文件来维护系统级的调度任务。
一般情况下,不建议将所有的全局性的调度任务都维护在/etc/crontab这一个文件中,那样会很混乱,因此有了/etc/cron.d/。
/etc/cron.d/是一个目录,也由root维护。这个目录下可以存放多个crontab任务文件,这样我们就可以以文件的粒度对不同类别的任务调度进行管理了。
/etc/cron.d/目录下的任务调度文件需要遵循以下命名规范才能被cron服务正确扫描:
/etc/cron.hourly、/etc/cron.dAIly、/etc/cron.weekly、/etc/cron.monthly 这些是什么?
系统预设了这四个目录,用来放置每小时、每天、每周、每月要执行的可执行脚本文件。
注意:这四个目录只是预设,并没有完全启用,并不是直接将可执行脚本文件放入其中就可以,还需要run-parts命令的配合。
笔者所使用的centos 7.2中,就只有 /etc/cron.hourly 这一个目录作为系统样例被启用了(配置在了/etc/cron.d/目录下的 0hourly 文件中)。
/var/spool/cron/是一个目录,用来存放包括root在内的每个用户独有的crontab任务文件,文件名同用户名,如用户“pcserver10”的任务文件为“
/var/spool/cron/pcserver10”。
/var/spool/cron/目录下的crontab任务文件不可以直接创建或直接修改,而是通过crontab命令来维护。
功能 |
命令 |
查看cron服务状态 |
service crond status |
启动cron服务 |
service crond start |
停止cron服务 |
service crond stop |
重启cron服务 |
service crond restart |
调度任务没有执行,我们首先要通过service crond status来确认cron服务是否已启动。
在调度任务突然失效的情况下,我们可以尝试service crond restart重启cron服务来解决问题。
crontab命令用来维护用户级的调度任务,其实就是维护/var/spool/cron/目录下对应用户的crontab任务文件,语法格式如下:
crontab [-u user] file
crontab [-u user] { -e | -l | -r }
复制代码
参数说明:
特别注意:
由于crontab -r删除任务表后不可恢复,因此强烈建议为任务表做好备份。
也就我们在维护任务表时,不要因为方便而使用crontab -e直接编辑任务表,而是应该将任务表的内容维护至一个备份文件中,再通过crontab file命令更新任务表,这样我们就能留有一份备份文件,以防误操作使用了crontab -r而无法恢复。
不管是/etc/crontab文件本身、/etc/cron.d/目录下的任务文件,还是通过crontab命令维护的/var/spool/cron/目录下的任务文件,这些文件的配置语法基本一致(仅/var/spool/cron/下的文件稍微有不同)。
以/etc/crontab为例,我们来看一下crontab任务文件的内容,可以将其分为环境变量和调度任务两个部分:
每一个cron任务的格式如下:
<分钟> <小时> <日期> <月份> <星期> [<用户>] <指令>
复制代码
首先来看各个时间配置项的取值范围:
配置项 |
取值范围 |
分钟 |
取值范围:0-59 |
小时 |
取值范围:0-23 |
日期 |
取值范围:1-31 |
月份 |
取值范围:1-12,也可以使用jan、feb、mar、apr、may、jun、jul、aug、sep、oct、nov、dec(不区分大小写) |
星期 |
取值范围:0-7(0与7均代表星期日),也可以使用mon、tue、wed、thu、fri、sat、sun(不区分大小写) |
除了上述的取值范围外,还支持以下特殊符号:
特殊符号 |
详细说明 |
*(星号) |
“每一个”的意思,代表着取值范围内的任何一个有效值都需要执行指令。 |
-(减号) |
用来表示时间范围(只支持数字,不支持用名称来表示的月份与星期),如:“3-6” |
,(逗号) |
可以用来分隔开一系列的值,来形成一个时间列表(只支持数字,不支持用名称来表示的月份与星期),如:“1,3,5,7”、“0-4,8-12” |
/n(斜线 + 数字) |
用来指定时间间隔与频率,也就是“每隔 n 个时间单位”的意思,如:“*/2”用在小时上代表着每2个小时、“0-29/2”用在分钟上代表着前半个小时中每2分钟 |
下面是一些例子:
表达式 |
说明 |
0 6 * * * |
每天早6点 |
0 */2 * * * |
每两个小时 |
0 23-7/2,8 * * * |
每晚11点到早8点间的每两个小时以及早8点 |
0 11 * * 1-3 |
每周一到周三的早11点 |
0 4 1 1 * |
1月1日的早4点 |
5,15,25,35,45,55 16,17,18 * * * |
每天下午4点、5点、6点的5分、15分、25分、35分、45分、55分 |
特别注意:
可以分别以周或以日月为单位,但不要使用 “几月几号且为星期几” 的模式,如:“0 6 1 1 3”。从语法上这个时间表达式并没有错误,可以运行。但是从语义上却存在歧义,我们可能希望1月1号且为周三时执行,但系统有可能会处理为每年1月1号与每个周三分别执行。
我们还可以用以下“昵称”来替代五个时间配置项:
昵称 |
详细说明 |
@reboot |
系统重启时执行一次指令 |
@yearly |
每年1月1号0点0分执行一次指令,即“0 0 1 1 *” |
@annually |
同@yearly |
@monthly |
每月1号0点0分执行一次指令,即“0 0 1 * *” |
@weekly |
每周日0点0分执行一次指令,即“0 0 * * 0” |
@daily |
每天0点0分执行一次指令,即“0 0 * * *” |
@hourly |
每小时0分执行一次指令,即“0 * * * *” |
指令可以有以下几种形式:
注意:%是crontab的特殊字符,第一个%后的所有数据都会被作为标准输入。在指令中如需使用%,则需要进行转义%,如:date '+%Y%m%d'。
crontab可配置的最小的时间粒度是分钟,那么我们如何实现秒级的任务呢?有如下两种方式:
* * * * * command
* * * * * sleep 10; command
* * * * * sleep 20; command
* * * * * sleep 30; command
* * * * * sleep 40; command
* * * * * sleep 50; command
复制代码
* * * * * script
复制代码
#!/bin/sh
STEP=5
for ((i=0; i < 60; i+=STEP))
do
TIME_NOW=$(date "+%Y-%m-%d %H:%M:%S")
echo -e $TIME_NOW >> /root/scripts/logs/test.log
sleep $STEP
done
复制代码
在crontab任务文件的头部,可以设置以下环境变量:
环境变量 |
说明 |
由crond设置的默认值 |
SHELL |
指定系统要使用哪种shell解释器 |
/bin/sh |
PATH |
指定系统执行命令的路径 |
/usr/bin:/bin |
MAILTO |
指定cron将任务执行的相关信息通过电子邮件发送给哪个用户 |
无 |
HOME |
指定执行命令或者脚本时使用的主目录 |
根据crontab的拥有者从/etc/passwd文件中获取 |
当我们的指令或脚本依赖于环境变量的时候,问题就出现了。在命令行中可以顺利运行的指令或脚本,在crontab中却无法正确执行。这是因为crontab不会从用户的profile文件中读取环境变量参数。
这时我们就会把目光转向PATH,直接设置这个变量不就可以了嘛。然而,这个变量似乎就是个摆设,并不会按照我们想象的那样去运行。
这里记录一下PATH的实验过程:
笔者的CentOS 7.2,系统默认安装了OpenJDK,版本为号为1.8.0.181。
笔者又重新配置了1.8.0_144版本的JDK,写入了/etc/profile系统环境变量中:
JAVA版本验证如下:
现在我们写一个定时任务,将java -version的输出结果写入日志中:* * * * * java -version 2>>
/root/scripts/logs/test_java.log 复制代码 看一下结果:
这不是我们想要的结果,很明显环境变量出了问题,我们把环境变量打印出来:
* * * * * echo -e "PATH="$PATH >>
/root/scripts/logs/test_java.log;java -version 2>> /root/scripts/logs/test_java.log 复制代码 再看结果:
环境变量果然不对,当前是crontab文档中说的由crond所设置的默认值。我们通过PATH手动修改一下吧:
PATH=
/sbin:/bin:/usr/sbin:/usr/bin:/usr/java/jdk1.8.0_144/bin:/usr/java/jdk1.8.0_144/jre/bin: * * * * * echo -e "PATH="$PATH >> /root/scripts/logs/test_java.log;java -version 2>> /root/scripts/logs/test_java.log 复制代码 再看结果:
环境变量倒是设置成功了,但是实际上并没有生效,所以,设置PATH感觉就是设置了个寂寞。
那么环境变量的问题该如何解决呢?有以下两种方式:
* * * * * . /etc/profile; your command
复制代码
#!/bin/sh
. /etc/profile
. ~/.bash_profile
复制代码
每一个调度任务执行完毕时,都会将标准输出与错误输出信息以电子邮件的形式发送给对应的用户,/var/spool/mail/目录下的与用户同名的文件用来存放对应用户所接收到的邮件信息,邮件内容如:
我们要讨论的第一个问题就是MAILTO所能起到的作用。
(1)在没有配置MAILTO的情况下:
(2)配置了MAILTO的情况下:
调度任务执行后发送邮件,这样日积月累,邮件文件会越来越大,严重的情况下可能会影响系统的运行。我们要讨论的下一个问题就是妥善的处理邮件信息。
上面已经提到了,我们可以通过定义一个空的MAILTO,来禁止发送邮件的行为。但MAILTO会作用于文件中所有的任务,是否有办法从单个任务的角度出发来解决这个问题呢?我们可以使用输出重定向,在每条指定后增加: >/dev/null 2>&1。(当前,这么做的前提是,对所需要的正常的输出已经做了处理,比如追加到某个日志文件中。)
虽然实际情况下,基本用不到也不建议使用HOME(建议所有的地方都使用绝对路径,以屏蔽因路径而引发的各类问题),但并不妨碍我们实践并体验一下HOME的作用。
我们在/root/scripts目录下做4个测试脚本,并将HOME的值设置为此目录,然后以4种不同的写法做测试:
测试结果如下:
上述测试过程仅为体验,依然建议:所有的地方都使用绝对路径!!!
作者:熙宁
链接:
https://juejin.cn/post/7065972818965430286