Linux 系统定时任务 Cron(d)服务应用实践
三、用户定时任务 Cron 实例说明
下面来看一下用户定时任务 Cron 的使用实例,示例代码如下:
命令实例 1:*/1 * * * * /bin/sh/scripts/data.sh
第一列的意思为分钟,特殊符号 "/" 表示每隔 的意思,即表示每隔一分钟执行一次/bin/sh/scripts/data.sh 程序。
命令实例 2:30 3,12 * * * /bin/sh/scripts/oldboy.sh
在本例中,第一列为 30,表示 30 分钟;第二列为 "3,12",表示 3 点和 12 点,此定时任务的意思是每天凌晨 3 点和中午 12 点的半点时刻 (或描述为每天凌晨 3:30 和中午 12:30)执行一次 /bin/sh/scripts/oldboy.sh 脚本任务。
命令实例 3:30 */6 * * * /bin/sh/scripts/oldboy.sh
第一列为 30,表示 30 分钟;第二列 "*/6" 代表每6个小时,相当于就是 6、12、18、24 的作用。此定时任务的意思是每隔 6 个小时的半点时刻执行一次 /bin/sh/scripts/oldboy.sh 脚本任务。
命令实例 4:30 8-18/2 * * * /bin/sh/scripts/oldboy.sh
第一列为 30,表示 30 分钟;第二列的 "8-18/2" 代表在早晨 8 点到下午 18 点之间每隔 2 小时,也相当于是将 8、10、 12、14、16、18 单独列出。此定时任务的意思就是早晨 8 点到下午 18 点之间,每隔 2 小时的半点时刻执行一次 /bin/sh/scripts/oldboy.sh 脚本任务。
命令实例 5:30 21 * * * /application/apache/bin/apachectl graceful
该例表示每晚的 21:30 重启 Apache。
命令实例 6:45 4 1,10,22 * * /application/apache/bin/apachectl graceful
该例表示每月 1、10、22 日的凌晨 4:45 分重启一次 Apache。
命令实例 7:10 1 * * 6,0 /application/apache/bin/apachectl graceful
该例表示每周六、周日的凌晨 1:10 分重启一次 Apache。
命令实例 8:0,30 18-23 * * * /application/apache/bin/apachectl graceful
该例表示在每天 18:00 至 23:00 之间每隔 30 分钟重启一次 Apache。
命令实例 9:00 */1 * * * /application/apache/bin/apachectl graceful
该例表示每隔一小时整点重启一次 Apache。
命令实例 10:* 23,00-07/1 * * * /application/apache/bin/apachectl graceful
本例并不是表示晚上 23 点和早上 0~7 点之间每隔一小时重启一次 Apache。 需要说明的是,以上结果是不规范的,也是不对的。以上定时任务的第一列为 "*",表示每分钟都执行一次任务,即晚上 23 点和早上 0~7 点之间每一分钟都重启一次 Apache,明显是错误的。
命令实例 11:00 11 * 4 1-3 /application/apache/bin/apachectl graceful
该例表示 4 月的每周一到周三的上午 11 点整重启一次 Apache。
命令实例 12:30 09 * * 0 去老男孩教育上课
该例表示每周日上午 9:30 去老男孩教育上课,这是周末班的上课频率。
命令实例 13:30 08 * * *去老男孩教育上课
该例表示每天上午 8:30 去老男孩教育上课,这是脱产班的上课频率。
四、生产环境下用户 Cron 配置专业实践案例
前文是通过定时任务规则来理解定时任务计划,下文是根据需求来设定定时任务规则的案例,读者可以自己解答一下,看是否搞懂了。
范例 13-1 :每分钟打印一次 oldboy 字符串到 "/server/log/oldboy.log" 中。
步骤 1:先在命令行实现要处理的任务,也就是先进行测试。
[root@shy ~]# mkdir server
[root@shy ~]# mkdir server/log
[root@shy ~]# tree server/ # 查看 server 目录结构。
server/
└── log
1 directory, 0 files
[root@shy ~]# touch server/log/oldboy.log
[root@shy ~]# tree server/
server/
└── log
└── oldboy.log
1 directory, 1 file
[root@shy ~]# echo oldboy>>server/log/oldboy.log # 将 oldboy 追加到文件内。
[root@shy ~]# cat server/log/oldboy.log # 查看执行后的结果。
oldboy
步骤 2:在 Linux 命令行执行 crontab -e。进入 vi 编辑文本的状态之后,输入如下内容:
[root@shy ~]# crontab -e # 按 i 进入 vim 的编辑模式。
no crontab for root - using an empty one
# print my name to log by oldboy at 202605 # 注释
* * * * * echo oldboy>>server/log/oldboy.log # 按 Esc 退出编辑模式,输入 :wq 保存退出。
步骤 3 :保存,查看编辑完的定时任务配置。以下是具体命令:
[root@shy ~]# crontab -l|head -2 # -l 查看定时任务的内容。
# print my name to log by oldboy at 202605
* * * * * echo oldboy>>server/log/oldboy.log
步骤 4 :观察生效情况。以下是具体命令:
[root@shy ~]# tail -f server/log/oldboy.log
oldboy
oldboy
oldboy
oldboy
oldboy
oldboy
提示 : 思考以及配置的规范过程比答案更重要,如果出现问题,请查看 /var/log/cron 排错。
知识小结:
- 先确认 Crond 服务进程是否开启。
- 书写定时任务规则前应尽量先写注释,以方便自己以及同事阅读。
- 这里的 server/log 目录必须要事先存在才能出结果,因此,在命令行测试执行成功很重要。
- 定时任务中的所有路径(包含文件和命令等的路径)都尽量使用绝对路径(本题中不加 echo 也可以)。
- 如果命令中有重定向符号等,那么结尾不要再加 >/dev/null 2>&1,否则会出错。
范例 13-2 :每天晚上 0 点,将站点目录 /var/www/html 下的内容打包备份到 /data 目录下,并且要求每次生成不同的备份包名。
步骤 1:确认要备份的数据目录和备份的目的地路径是否存在,如果不存在就创建一个,以便测试。
[root@shy ~]# ls -ld /var/www/html /data
drwxr-xr-x. 7 root root 143 3月 24 15:44 /data
drwxr-xr-x 3 root root 90 5月 30 2023 /var/www/html
[root@shy ~]# touch /var/www/html/oldboy{1..5}.txt
[root@shy ~]# ls /var/www/html/
oldboy1.txt oldboy2.txt oldboy3.txt oldboy4.txt oldboy5.txt
步骤 2 :备份数据,一般是采用压缩打包的形式。以下是命令行测试:
[root@shy ~]# cd /var/www/ # 打压缩包,最好是到备份数据目录的上一级目录打包。
[root@shy www]# tar zcvf /data/bak_$(date +%F).tar.gz ./html # 带日期打包生成不同次备份文件
./html/
./html/oldboy1.txt
./html/oldboy2.txt
./html/oldboy3.txt
./html/oldboy4.txt
./html/oldboy5.txt
[root@shy www]# ls -l /data
-rw-r--r-- 1 root root 270 5月 27 13:14 bak_2026-05-27.tar.gz
步骤 3:将测试成功的命令写入到文件里执行。注意,定时任务执行的命令最好是以 Shell 脚本的形式来执行,这样可以规避很多潜在的运行错误,例如,由 "%" 导致的错误,代码如下:
[root@shy scripts]# cat bak.sh
cat: bak.sh: 没有那个文件或目录
[root@shy scripts]# ls # 该目录下没有 bak.sh 文件
[root@shy scripts]# touch bak.sh # 添加该文件
[root@shy scripts]# vim bak.sh # 编辑该文件
cd /var/www/&&\
/bin/tar zcf /data/bak_$(date +%F).tar.gz ./html
[root@shy scripts]# cat bak.sh # 查看编辑后的脚本内容,即命令行的命令集合。
cd /var/www/&&\ # && 表示本条命令成功之后再执行下面的 tar 命令,\ 表示换行。
/bin/tar zcf /data/bak_$(date +%F).tar.gz ./html # 注意:这里去掉了 -v 参数,即不输出信息,打包的文件名中使用了日期变量,这样才能按天生成不同的压缩包文件。
[root@shy scripts]# rm -f /data/bak_2026-05-27.tar.gz # 删除先前生成的备份文件。
[root@shy scripts]# /bin/sh /server/scripts/bak.sh # 使用 /bin/sh 加全路径执行脚本。
[root@shy scripts]# ls -l /data
-rw-r--r-- 1 root root 270 5月 27 13:31 bak_2026-05-27.tar.gz # 测试结果依然正确。
步骤 4 :编写定时任务。在 Linux 命令行执行 crontab -e,进入 vi 编辑状态之后,输入以下内容:
[root@shy scripts]# crontab -e
# print my name to log by oldboy at 202605
* * * * * echo oldboy>>server/log/oldboy.log
# backup site dir by shy at 202605
00 00 * * * /bin/sh /server/scripts/bak.sh >/dev/null 2>&1
[root@shy scripts]# crontab -l | tail -2
# backup site dir by shy at 202605
00 00 * * * /bin/sh /server/scripts/bak.sh >/dev/null 2>&1 # 结尾要加 >/dev/null 2> &1,将所有输出定向到空。
五、生产环境下的定时 Cron 书写要领
1、为定时任务规则加上必要的注释
书写定时任务规则时应尽可能地加上注释(最好是英文注释),这是个很好的运维习惯和规范。例如,什么人,在什么时间,因为谁 (需求方),做了什么定时任务计划。如果这些都标记清楚了,那么其他的运维人员(同事)可以很容易理解任务的信息,从而提升团队工作的效率。若不写注释,则会给以后的维护和任务交接带来麻烦。带注释的定时任务示例代码如下:
[root@shy scripts]# crontab -l | tail -2
# backup site dir by shy at 202605 # 注释
00 00 * * * /bin/sh /server/scripts/bak.sh >/dev/null 2>&1
2、所有的定时任务尽量都以脚本的形式执行
如果定时任务计划直接使用 Linux 命令执行,不但看着不规范,而且也很容易出错,特别是带系统时间变量(如果含有 "%",则必须要转义,即 "%")的任务命令,如果能以文件的形式书写,则可以减少很多潜在的错误,并提升效率、规范,这是一个好习惯。在定时任务中执行命令也会有一些限制,如时间变量问题,多个重定向命令混用问题等,示例代码如下:
[root@shy scripts]# crontab -l | tail -2
# backup site dir by shy at 202605
00 00 * * * /bin/sh /server/scripts/bak.sh >/dev/null 2>&1 # 写成脚本文件执行最佳
[root@shy scripts]# cat bak.sh # 脚本文件内容如下
cd /var/www/&&\
/bin/tar zcf /data/bak_$(date +%F).tar.gz ./html
3、在执行的 Shell 脚本前加上 /bin/sh
要确保 Cron 对应的执行者有访问 Shell 脚本所在目录的权限 ,并且可执行 该 Shell 脚本(可用 chmod 和 chown 修改脚本权限和所有者)。当然,最佳方法是在要执行的任务脚本前加上 /bin/sh ,然后执行,否则就有可能会因为忘了为脚本设定执行权限,而无法完成当次任务执行计划。本条要领是一个经验型的好习惯。执行其他语言的脚本也要加上对应语言的解释器,以下是相应的执行方法示例。
规范的Shell脚本定时任务执行方法:
/bin/sh/server/scripts/bak.sh
规范的Perl脚本定时任务执行方法:
/usr/bin/perl/server/scripts/bak.sh
规范的Python脚本定时任务执行方法:
/usr/bin/python/server/scripts/bak.py
4、定时任务中在命令或脚本的结尾加上 >/dev/null 2>&1
定时任务(一般是脚本任务)规则的结尾最好加上 ">/dev/null 2>&1",如果需要打印日志,则可以追加到指定的 日志文件里(此时不要与 /dev/null 同时存在),总之,定时任务计划脚本的结尾尽量不要留空。因为在默认情况下,定时任务每一次执行完毕之后,都会向对应的用户发邮件,如果不加将输出(正确 或错误)定向到空的内容(>/dev/null 2>&1),则可能会由于系统未开启邮件服务而导致邮件临时目录文件数猛增的隐患发生,大量小文件占用磁盘 Inode 节点数量(每个文件占一个 Inode),以致磁盘 Inode 写满而无法再写入正常数据(故障提示: no space left on device.)的故障发生。
在 ">/dev/null 2>&1" 中,">" 表示重定向,"/dev/null" 为特殊的字符设备文件,表示黑洞设备或空设备。"2>&1" 表示让标准错误和标准输出一样,本命令的意思是将前面脚本的正常和错误输出都重定向到/dev/null ,也就是什么都不输出。下面三种让标准错误和标准输出都重定向到空的写法是等价的:
>/dev/null 2>&1 等价于 1>/dev/null 2>/dev/null 等价于 &>dev/null
5、在指定用户下执行相关定时任务
需要 root 权限执行的任务可以登录到 root 用户下然后进行设置,如果执行任务不需要 root 权限,则可以登录到普通用户下(也可以直接在 root 下通过命令 crontab -u oldboy -e 直接设置)进行设置。这里需要特别注意不同用户的环境变量问题,如果是调用了系统环境变量,例如 /etc/profile 等文件下的变量(如生产场景中 Java 程序 的定时任务计划),那么最好是在程序脚本中将用到的环境变量重新 export 下(下文有案例)。
6、生产任务计划程序中不要随意打印输出信息
在开发定时任务程序或脚本时,调试好脚本程序之后,应尽量将 Debug 及命令输出的内容信息屏蔽掉,如果确实需要输出日志,则可定向到指定的日志文件里,以避免随意输出不做重定向,从而导致系统垃圾的产生。
例如,有的人打包时喜欢用 tar 命令的 zcvf 这几个参数。其中的 v 参数就是用于查看打包信息的。在做定时任务计划时,命令里就不要再带这个参数了。
7、定时任务执行的脚本要存放到规范路径下
定时任务执行的脚本要存放到规范路径下,其实,系统中所有的脚本存放都要有规范,这里推荐统一使用 /server/scripts 作为脚本的存放路径。
8、配置定时任务要规范操作过程,减少出错
配置定时任务时,规范的操作过程具体如下。
- 尽量先在命令行测试成功,然后将成功的命令复制到脚本里,新手要在各个细小环节减少出错的机会。
- 然后执行测试脚本,测试成功后,将执行脚本的命令完整复制到定时任务配置里,尽量避免手动输入命令。
- 先在测试环境下进行测试,然后在正式环境下规范部署。
- 要有检验任务是否正确执行的手段,例如,检查 /var/log/cron 日志文件,如果任务执行计划频率较低,也要想法确保任务的可执行性,此处可见下文调试定时任务的技巧。
9、定时任务脚本中程序命令及路径尽量使用全路径
定时任务脚本中,程序命令及路径应尽量使用全路径,这是个防止定时任务执行错误的好习惯,否则可能会导致命令行操作命令及执行脚本是正常的,但是放到定时任务中却无法正确执行的问题。当然,对于命令,除了写全路径之外,还可以在脚本中重新定义 PATH 环境变量,示例代码如下:
[root@shy scripts]# crontab -l | tail -2
# backup site dir by shy at 202605
00 00 * * * /bin/sh /server/scripts/bak.sh >/dev/null 2>&1 # /bin/sh 命令使用全路径。
[root@shy scripts]# cat bak.sh # 脚本文件内容如下
cd /var/www/&&\
/bin/tar zcf /data/bak_$(date +%F).tar.gz ./html # /bin/tar 备份命令使用全路径。
重新定义 PATH 环境变量的方法如下:
[root@shy scripts]# echo $PATH # 打印输出,根据输出选择路径进行重新定义。
root/.pyenv/shims:/root/.pyenv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@shy scripts]# cat bak.sh
export PATH='/sbin:/bin:/usr/sbin:/usr/bin' # 重新定义环境变量,包含脚本中所有执行命令所在的路径。
cd /var/www/&&\
tar zcf /data/bak_$(date +%F).tar.gz ./html # 这里可以取消全路径了。
10、时间变量 % 号要使用反斜线转义
"%" 号在 Cron 任务配置中被认为是 newline,要用 "\" 来转义。使用 crontab 编辑任务时,如果有类似于 "date+%F" 的时间变量,则必须做如下转义 "date+%F",但是如果是在脚本中编写, 那么 "%" 就不需要转义了。这也是笔者推荐定时任务使用脚本文件来执行的原因之一,定时任务使用命令执行时,执行命令中带有时间变量的写法(不建议使用此法)示例如下:
# tar comment by shy at 202605
*/1 * * * * tar zcf /data/bak_$(date +\%F).tar.gz /var/www/html &>/dev/null
11、若脚本中调用了系统环境变量,则要重新定义
crontab 执行 Shell 等脚本时只能识别很少的系统环境变量,用户在 /etc/profile 等文件中定义的普通变量一般是无法被定时任务服务识别的,如果在编写的脚本中需要使用这些环境变量,那么最好是使用 export 重新声明下该变量,这样脚本才能正常执行。
例如,在调试 Java 程序任务计划的时候,需要多注意环境变量的问题,务必要将环境变量的定义加到定时任务计划脚本里。Task.sh 的作用是执行 Java 相关的程序,这里需要在脚本中重新定义相关的环境变量,示例代码如下:
[root@shy scripts]# mkdir /scripts/resin/shell -p # 递归创建目录
[root@shy scripts]# touch /scripts/resin/shell/Task.sh # 添加文件
[root@shy scripts]# cat /scripts/resin/shell/Task.sh
[root@shy scripts]# vim /scripts/resin/shell/Task.sh # 编辑文件
export JAVA_HOME=/application/jdk1.6 # 如下三行是安装 Java 相关环境需要的特殊变量。
export PATH=$JAVA_HOME/bin:$PATH
export SH_HOME=/application/resin/webapps/ROOT/
export LIB=$SH_HOME/WEB-INF/lib
# JAVA Shell by oldboy 202605
00 9,14 * * * nohup /scripts/resin/shell/Task.sh & >/app/log.log 2>&1
[root@shy scripts]# cat /scripts/resin/shell/Task.sh
export JAVA_HOME=/application/jdk1.6
export PATH=$JAVA_HOME/bin:$PATH
export SH_HOME=/application/resin/webapps/ROOT/
export LIB=$SH_HOME/WEB-INF/lib
# JAVA Shell by oldboy 202605
00 9,14 * * * nohup /scripts/resin/shell/Task.sh & >/app/log.log 2>&1