一文精通 crontab 从入门到出坑

此篇技术博文主要介绍的是crontab,Linux下的计划任务管理工具。涉及内容包括crontab使用配置、常见坑的分析和编者总结的错误调试方法。php

个人理解,后台任务一般分为两种:常驻和定时。以前的文章pm2 进程管理工具使用总结主要针对的是常驻任务。今天来谈谈crontab,主要针对的是定时任务。html

实验环境:centos7node

介绍crontab

crontab的服务进程名为crond,英文意为周期任务。顾名思义,crontab在Linux主要用于周期定时任务管理。一般安装操做系统后,默认已启动crond服务。crontab可理解为cron_table,表示cron的任务列表。相似crontab的工具还有at和anacrontab,但具体使用场景不一样,可参见附录《让你学会Linux计划任务》一文了解更多。程序员

关于crontab的用途不少,如shell

  • 定时系统检测;
  • 定时数据采集;
  • 定时日志备份;
  • 定时更新数据缓存;
  • 定时生成报表;
  • 其余一些定时任务

固然,更多使用场景是要以视具体状况而定了。毕竟是工具一般都是经常使用规则总结而成的产物。vim

确认crond服务已经安装与开启以后,下面开始具体说明centos

简单示例

先来个简单示例体验一下。缓存

  • 目标:每分钟向/tmp/time.txt文件下写入当前时间
  • 新建crontab任务
$ crontab -e      // 打开crontab任务编辑
* * * * * date >> /tmp/time.txt
复制代码
  • 静静等待几分钟
$ cat /tmp/time.txt
Do 29. Dez 22:45:01 CST 2016
Do 29. Dez 22:46:01 CST 2016
Do 29. Dez 22:47:01 CST 2016
复制代码
  • 从上面结果看出,每分钟执行了date并写入到/tmp/time.txt。

简单示例演示成功。下面从细节深刻说明crontab使用。bash

使用选项

上面的实验中使用了crontab命令的-e选项。咱们来看看crontab命令中有哪些选项?服务器

-e 选项 表示打开当前用户的crontab任务列表配置文件。固然也能够直接打开,路径一般是在/var/spool/cron/下,文件以用户名命名,如/var/spool/cron/root。不过,采用-e方式打开,福利是能够帮助咱们自动检查任务配置符合规则。

-u 选项 指定某用户的任务列表,很好理解。好比我当前是root用户,想操做poloxue用户的任务列表。以下:

$ crontab -u poloxue -e
复制代码

-l 选项 列出某用户的全部任务列表 -r 选项 删除某用户的全部任务列表,这个选项使用当心为上,估计也只是本身实验时玩玩而已,正常不使用。

crontab命令的选项中,主要使用的就是以上几个,理解比较简单。

任务配置

说完了crontab的命令选项,下面开始真正的大戏,任务列表文件如何配置?

首先,看下crontab任务列表配置格式,示例文件以下:

SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# 更多细节 man 4 crontabs

# 计划任务定义的例子:
# .---------------- 分 (0 - 59)
# | .------------- 时 (0 - 23)
# | | .---------- 日 (1 - 31)
# | | | .------- 月 (1 - 12)
# | | | | .---- 星期 (0 - 7) (星期日可为0或7)
# | | | | |
# * * * * * 执行的命令
* * * * * date >> /time.txt 2>&1
复制代码

从上面的示例文件可看出,crontab的任务列表主要由两部分组成:环境变量配置与定时任务配置。可能你们在工做中更可能是只用到了任务配置部分。

环境变量配置部分

理解环境变量配置这部分能够帮助咱们减小去踩一些没必要要的坑。简单说明上面涉及的环境变量。

SHELL为/bin/bash,表示使用/bin/bash解释执行命令

PATH表示到哪些目录路径寻找命令程序,此环境变量的值说明了为何咱们在crontab中执行命令时,尽可能要写命令全路径才能执行的缘由。

MAILTO变量做用是当任务执行有输出时,内容发送到哪一个用户的邮箱。禁用能够设置MAILTO=""。

当咱们在使用crontab时,发现某些定时任务不能顺利执行,但shell控制台执行成功,环境变量是否正确是咱们须要首先关注的点之一。具体详情能够看后面关于环境变量坑的说明。

定时任务配置部分

这部分是crontab配置核心。

基本配置

以下所示配置共6列,前5列是关于执行时间配置,最后1列是具体执行命令。

.---------------- 分 (0 - 59)
|  .------------- 时 (0 - 23)
|  |  .---------- 日 (1 - 31)
|  |  |  .------- 月 (1 - 12)
|  |  |  |  .---- 星期 (0 - 6) (星期日可为0或7)
|  |  |  |  |
*  *  *  *  * 执行的命令
复制代码

第一列单位为分,表示每时第几分钟,范围为0-59; 第二列单位为时,表示天天第几小时,范围为0-23; 第三列单位为日,表示每个月第几天,范围为1-31; 第四列单位为月,表示每一年第几月,范围为1-12; 第五列单位为星期,表示每星期第几天,范围0-7,0与7表示星期日,其余分别为星期1-6;

时间配置段类型

根据时间列中值的不一样设置方式,编者总结出如下五种类型:

固定某值,指定固定值,如指定1月1日0时0分执行任务

0 0 1 1 * command
复制代码

月日时分都指定了固定数值。 注:*在crontab中表示任意值都知足条件。

列表值,时间值是一个列表,如指定一个月内二、十二、22日零时执行任务

0 0 2,12,22 * * command
复制代码

上述日指定多个值,2号、12号和22号,以逗号分隔;

连续范围值,时间为连续范围的值,如指定每月1至7号零时执行任务

0 0 1-7 * * command
复制代码

上述日期为连续范围的值1-7时

步长值,根据指定数值跳跃步长肯定执行时间,如指定凌晨1时开始每割3个小时0分执行一次任务

0 1-24/3 * * * command
复制代码

上述指定从凌晨1时每3个小时执行任务,如1点0分,4点0分,7点0分等。

混合值,支持以上类型的组合,如指定每小时0至10分,2二、33分以及0-60分钟每隔20分钟执行任务,以下

0-10,22,33,*/20 * * * * command
复制代码

这里的分钟值采起了多种类型组合指定,包括连续范围值(0-7),列表值(22,33),步长值(*/20)。

声明:这几种时间配置类型是编者本身总结,但愿能帮助你们更好理解。有错误帮忙指出。

定时语句解析工具

一般在使用crontab添加任务时,咱们会依靠本身已有知识编写定时语句。当须要测试语句是否正确时,总须要必定时间等待证实其正确性。做为一名牛逼的程序员,这种方式就太不酷了。有没有一款工具,只要咱们给出语句,其就能告诉具体执行时间呢?下面介绍一款老外开发的crontab在线解析工具。

工具地址:crontab.guru

下面是这个工具的截图

crontab_tool.png

从上面看出,咱们输入的语句解析结果为天天的04:05执行任务。下面有这样一行文字“next at 2016-12-31 04:05:00”,告诉了咱们最近一次的执行时间。

注明:百度搜索“crontab在线解析”得到的工具备坑,某些语句解析结果错误。为避免你们受骗,这里提供具体地址:tool.lu/crontab/

使用有坑

crontab使用中常会遇到各类坑。下面列出编者在使用中曾遇到的一些问题。

时间配置误区

此处介绍两种坑,一种是因为基本功不足致使配置错误,而另外一种则是多数人对crontab配置都存在的一个理解误区。

整点时间设置错误

其实这个错误不用单独说明,可是编者刚开始接触crontab时犯过,单独拿出来讲明一下。

如设定天天3点执行一次某任务

下面列出错误方式,当咱们听到天天3点执行一次某任务时,不少人会把重点放在3点,而忽略了执行一次的需求。

下面是个错误的例子

* 3 * * * command
复制代码

这里会致使在三点的每分钟都会执行一次任务,也就是执行了60次。 正确方式以下,天天3点0时执行任务

0 3 * * * command
复制代码

日与星期的关系误区

这真的是个大误区,不少人都不知道的大误区。直接开始说明吧。

好,首先作两个练习

设置任务一:每个月的1-7天天零时执行某任务,答案以下:

0 0 1-7 * * date >> /tmp/date.txt
复制代码

设置任务二:每星期的星期一零时执行某任务,答案以下:

0 0 * * 1 date >> /tmp/date.txt
复制代码

上面两个任务的设定都是正确的。

下面提出第三个任务,设置每月的第一个星期一零时执行某任务

分解任务要求,首先,第一个星期就是每月的1-7日,而星期一就是星期一。因此咱们理解的crontab任务配置以下

0 0 1-7 * 1 date >> /tmp/date.txt
复制代码

下面直接使用前面介绍的在线解析工具分析此语句,以下

crontab_time.png

解析结果显示语句执行时间为每个月的1至7日和每星期一。能够看到最近执行时间是“next at 2017-01-01 00:00:00”,这个时间也并不是星期一。

这是crontab的一个特别容易误解之处,下面直接给出结论:

  • 当日和星期任一列包含*时,日与星期二者为而且的关系;
  • 当日和星期列中不包含*时,日与星期二者为或者的关系;

请注意,前面提到的那个百度搜索出来的工具分析结果显示的确是每个月第一个星期一,这是错误的。若有朋友持怀疑态度,可自行验证,若有错误,随时告知。

环境变量问题

当咱们刚使用crontab时,有人会告知全部命令尽可能都使用绝对路径,以防错误。为何?这就和咱们下面要谈的环境变量有关了。

首先,获取控制台环境变量看下

$ env
XDG_SESSION_ID=10
HOSTNAME=localhost.localdomain
SHELL=/bin/bash
PERL_MB_OPT=--install_base /root/perl5
USER=root
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php5/bin
PWD=/var/mail
SHLVL=1
HOME=/root
LOGNAME=root
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/env
复制代码

考虑篇幅,输出有删减。

而后,获取crontab环境变量信息

* * * * * /usr/bin/env > /tmp/env.txt
复制代码

输出结果,以下

$ cat /tmp/env.txt
XDG_SESSION_ID=732
SHELL=/bin/sh
USER=root
PATH=/usr/bin:/bin
PWD=/root
LANG=de_DE.UTF-8
SHLVL=1
HOME=/root
LOGNAME=root
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/en
复制代码

对比分析二者输出

对比crontab与控制台输出,咱们发现二者的环境变量差别很大。若是命令在控制台执行成功,而在crontab执行失败,咱们须要考虑是否命令涉及的环境变量在crontab和控制台间存在差别。

明白crontab使用绝对路径执行命令缘由了吗?

咱们知道命令默认查找路径是由PATH指定的。

从上面输出结果可知,控制台的PATH值为

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php/bin
复制代码

crontab的PATH值为

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/php/bin
复制代码

crontab的PATH值为

PATH=/usr/bin:/bin
复制代码

/usr/local/php/bin/下面存在php命令,在控制台执行成功

$ php index.php
复制代码

因在crontab的PATH变量无/usr/local/php/bin/,其执行php命令则会失败。

解决方式

已知哪一个环境变量致使问题,能够直接在crontab配置中加入变量配置。

不知哪一个环境变量致使问题,终极大招是引入控制台环境变量,以下

* * * * * source /$HOME/.bash_profile && command
复制代码

固然,对于某特定环境变量或有特定的处理方式,如PATH,命令使用绝对路径亦可解决。

特殊符号%

%在crontab是特殊符号,具体含义以下:

第一个%表示标准输入的开始

* * * * * cat >> /tmp/cat.txt 2>&1 % stdin input
复制代码

执行成功以后,查看/tmp/cat.txt

$ cat /tmp/cat.txt
stdin input
复制代码

咱们看到标准输入写入到了/tmp/cat.txt文件。

理解上面示例,首先需知cat >> /tmp/cat.txt ,做用是将标准输入重定向至/tmp/cat.txt。

其他%表示换行符

示例以下

* * * * * cat >> /tmp/cat_line.txt 2>&1 % stdin input 1 % stdin input 2 % stdin input 3
复制代码

查看输出

$ cat /tmp/cat_line.txt
stdin input 1
stdin input 2
stdin input 3
复制代码

有三行输出

解决方式

既然是特殊字符,天然而然就想到了使用\进行转义,以下:

* * * * * cat >> /tmp/cat_special.txt 2>&1 % per cent is \%. 2>&1
复制代码

查看输出

$ cat /tmp/cat_special.txt
per cent is %.
复制代码

执行成功了。自此,你就顺利爬出了%特殊字符问题的坑。

关于这个问题的具体说明,能够参看附录中的《Crontab and %》。

关于输出重定向

当咱们不作输出重定向时,如任务有大量输出,或许有些没法解释的问题。

输出写入邮件

crontab任务输出默认写入到执行用户的邮件中,以下演示:

* * * * * date
复制代码

命令输出当前日期,下面查看当前用户的邮件

$ cat /var/spool/mail/$USER
...

Sat Dec 31 17:45:01 CST 2016
复制代码

因而可知,任务输出的日期信息写入到了用户邮件中。

如任务有大量输出,会占用磁盘资源。但编者测试显示,如磁盘容量不足,任务也会执行,但输出不会写入邮件;

关闭邮件功能

如何关闭?设置MAILTO环境变量为空。以下

MAILTO=""
* * * * * date
复制代码

是否是关闭邮件写入就行了?附录《Linux中的crontab与sendmail》博文代表,关闭mail功能,输出内容将写入到/var/spool/clientmqueue中,可能占满分区的inode资源,致使任务没法执行。inode资源使用状况可经过以下命令获取

$ df -i
Filesystem Inodes   IUsed  IFree    IUse% Mounted on
/dev/sda1  512000   378    511622   1%    /boot
/dev/sda2  92672000 185351 92486649 1%    /
复制代码

抱歉!这种状况编者并未测出!但在公司的生产环境发现过未重定向则任务不执行的状况,加上后解决了问题。百度也搜索到了相似问题,若有朋友了解,欢迎指教,万分感谢。

固然,为了不此类问题发生,建议任务都加上输出重定向,以下

* * * * * date >> /dev/null/ 2>&1
复制代码

输出到/dev/null中,标准输入和标准错误都应处理。

如你们对重定向有疑惑,可参见附录中的《Linux重定向》,对文解释不错。

程序员的感悟:在技术的世界,当咱们不按常理作事,事情也不会按常理犯错。

调试大招

最后的福利,编者根据本身的总结而梳理出一套快速定位crontab错误的思路。两个角度:

  • 任务是否执行
  • 命令是否正确

任务是否执行?

调试思路

首先,经过日志确认任务是否执行 而后,如未执行则分析定时语句, 最后,定时没有问题,检查crond服务是否开启

下面说明具体分析步骤。

日志确认

调试错误,日志一般是个利器,crontab也有日志。 编者的服务器中crontab日志文件位置为/var/log/cron

查看日志 日志中包含任务执行记录,配置错误提示,任务配置编辑重载记录,服务开启等记录。

下面是日志的部份内容,

$ vim /var/log/cron
...
Dec 31 19:17:01 localhost crond[1455]: (CRON) bad day-of-week (/var/spool/cron/root)
Dec 31 19:17:01 localhost CROND[4409]: (root) CMD (date)
...
复制代码

这里截取了对调试比较重要的两条记录,以下介绍

执行记录

Dec 31 19:17:01 localhost CROND[4409]: (root) CMD (date)
复制代码

显示12月21 19时17分1秒执行了date命令

配置错误

Dec 31 19:17:01 localhost crond[1455]: (CRON) bad day-of-week (/var/spool/cron/root)
复制代码

上面显示/var/spool/cron/root的任务配置有错,也就是root任务配置有错。错误缘由:bad day-of-week,星期配置有错。

语句是这样的

* * * * date >> /dev/null 2>&1
复制代码

明显缺乏了星期时间段。

确认定时语句

经过上面的日志分析,如任务没有执行,使用定时语句在线分析工具分析定时是否正确,很是简单。

确认服务开启 若是定时语句也正确,检查服务是否开启。检测命令以下

Systemd方式(centos7及以上)

$ systemctl status crond.service
复制代码

SysVinit方式(centos7如下)

$ service crond status
复制代码

查看命令输出,如未开启,执行以下命令开启

Systemd方式(centos7及以上)

$ systemctl start crond.service
复制代码

SysVinit方式(centos7如下)

$ service crond start
复制代码

确认任务成功后,如问题仍未解决,继续往下看。

命令是否正确

确认命令成功与否,这里总结步骤大体以下

获取命令执行输出

crontab中的命令执行出错,多数人都不知道如何调试。咱们知道在控制台执行命令时,可经过输出获取错误信息调试问题。这种方式在crontab一样适用,方法就是利用从新向获取输出,进行分析。示例以下

* * * * * php /root/index.php >> /tmp/debug.log 2>&1
复制代码

这条任务老是执行失败,咱们把输出重定向到/tmp/debug.log。

查看debug.log,以下

$ cat /tmp/debug.log
/bin/sh: php: command not found
/bin/sh: php: command not found
复制代码

显示php命令没有找到,很明显的就能够肯定是环境变量的问题。这种方式定位问题很是有效。

具体问题具体分析

有了命令执行的输出,下面就是具体问题具体分析了。或许是前面提到的各类坑,也或许是命令自己所独有的问题。

调试的方法到这里就说完了。但仍是实践为王,需持续总结,同时也但愿你们不要在一样的坑中重复犯错。

crontab写了这么长,但愿能切实帮到你们。有哪位朋友看到了最后吗?表示佩服!

参考附录

相关文章
相关标签/搜索