服务器运维

crontab 命令详解:定时任务格式、参数与配置示例

crontab 是 Linux 定时任务的配置表,每行写“分 时 日 月 周”五个时间字段加一条命令,cron 服务每分钟检查一次、到点执行。crontab -e 编辑,-l 查看,-r 删除全部任务且不会确认;系统级任务写在 /etc/crontab 或 /etc/cron.d/,每行比用户 crontab 多一个用户名字段。

作者 顶联产品团队发布于 6 分钟阅读

crontab 是 Linux 上配置定时任务的命令,也是这张任务表本身的名字。每个用户有一张自己的 crontab,每行一个任务,格式是“分 时 日 月 周 命令”,例如 30 2 * * * /usr/local/bin/backup.sh 表示每天 2:30 执行备份脚本。cron 服务(RHEL 系叫 crond,Debian 系叫 cron)每分钟检查一次所有任务表,时间对上就执行。日常操作只需要四个参数:crontab -e 编辑,crontab -l 查看,crontab -r 删除,crontab -u 用户名 指定用户。写定时任务最容易出问题的往往不是时间格式,而是执行环境:cron 里的 PATH 很短、不读 .bashrc、% 有特殊含义,后文逐一说明。

crontab 参数说明:-e、-l、-r、-u

命令 作用 说明
crontab -e 编辑当前用户的定时任务 用 VISUAL 或 EDITOR 环境变量指定的编辑器打开,保存退出后自动检查语法并安装
crontab -l 查看当前用户的定时任务 没有任务时提示 no crontab for 用户名
crontab -r 删除当前用户的全部定时任务 不做确认,直接清空
crontab -ri 删除前先询问 cronie 和 Debian 的 cron 都支持 -i
crontab -u 用户 -l、crontab -u 用户 -e 查看、编辑指定用户的任务 只有 root 能用
crontab 文件名 用文件内容替换当前用户的任务表 原有任务被整体覆盖

crontab -e 怎么编辑

  1. 执行 crontab -e。Ubuntu 第一次执行时会让你选择编辑器,选 nano 最简单;想换成 Vim,执行 select-editor 重新选,或者临时用 EDITOR=vim crontab -e。
  2. 在文件末尾新起一行写任务,例如 */5 * * * * /usr/local/bin/check.sh >> /var/log/check.log 2>&1。
  3. 保存退出。nano 按 Ctrl+O、回车保存,Ctrl+X 退出;Vim 输入 :wq。看到 crontab: installing new crontab 就说明已经生效,不需要重启 cron 服务。
  4. 格式写错时会提示 errors in crontab file, can't install,并询问是否重新编辑,回答 y 回去修改。
  5. 执行 crontab -l,确认任务已经在表里。

几条使用习惯:

  • 改之前先备份。 -e 和 -r 在键盘上挨着,敲错一个字母就清空了全部任务:crontab -l > ~/crontab.bak.$(date +%F)。
  • 脚本里追加任务不用打开编辑器:(crontab -l 2>/dev/null; echo '0 4 * * * /usr/local/bin/cleanup.sh') | crontab -。
  • 不要直接改存放文件。 用户的任务表实际保存在 /var/spool/cron/用户名(RHEL 系)或 /var/spool/cron/crontabs/用户名(Debian 系),要通过 crontab 命令修改,命令会检查语法,cron 也会在下一分钟识别到变化。
  • 谁能用 crontab,由 /etc/cron.allow 和 /etc/cron.deny 控制:存在 cron.allow 时,只有其中列出的用户可以使用。

五个时间字段怎么写

┌───────────── 分钟(0–59)
│ ┌─────────── 小时(0–23)
│ │ ┌───────── 日(1–31)
│ │ │ ┌─────── 月(1–12,也可写 jan–dec)
│ │ │ │ ┌───── 星期(0–7,0 和 7 都表示周日,也可写 sun–sat)
│ │ │ │ │
* * * * * 要执行的命令

四个特殊符号:

符号 含义 例子
* 该字段的任意值 小时写 * 表示每个小时
, 列举多个值 日写 1,15 表示 1 号和 15 号
- 范围 小时写 9-18 表示 9 点到 18 点
/ 步长 分钟写 */10 表示每 10 分钟;0-30/10 表示 0、10、20、30 分

常用写法速查:

表达式 含义
*/5 * * * * 每 5 分钟
0 * * * * 每小时整点
30 2 * * * 每天 02:30
0 9-18 * * 1-5 周一到周五,9 点到 18 点的每个整点
0 3 * * 0 每周日 03:00
0 0 1 * * 每月 1 日 00:00
0 4 1,15 * * 每月 1 日和 15 日的 04:00
*/10 0-6 * * * 0 点到 6 点之间每 10 分钟
@reboot cron 服务启动时执行一次,相当于开机执行
@hourly、@daily、@weekly、@monthly 分别等于 0 * * * *、0 0 * * *、0 0 * * 0、0 0 1 * *

三个容易写错的地方:

  • 日和星期同时指定时是“或”的关系。 0 0 13 * 5 不是“13 号并且是周五”,而是每月 13 号和每个周五都会执行。两个字段中有一个是 * 时,才只按另一个字段判断。
  • 步长不会跨周期延续。 日写 */7 是每月的 1、8、15、22、29 号,到下个月又从 1 号开始,并不是严格的每 7 天一次。
  • 没有“每月最后一天”的写法。 可以让 28 到 31 号都触发,再在命令里判断明天是不是 1 号:0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = "01" ] && /usr/local/bin/month-end.sh。

/etc/crontab、/etc/cron.d 与用户 crontab 的区别

位置 怎么修改 每行格式 以谁的身份执行 适合放什么
用户 crontab crontab -e 5 个时间字段 + 命令 任务表的所有者 某个用户或某个应用自己的任务
/etc/crontab root 直接编辑文件 5 个时间字段 + 用户名 + 命令 行内指定的用户 系统自带的调度,一般不动
/etc/cron.d/ 下的文件 root 新建文件 同 /etc/crontab 行内指定的用户 自己的系统级任务,软件包、自动化部署写入的任务
/etc/cron.hourly、cron.daily、cron.weekly、cron.monthly 放入可执行脚本 不是 crontab 格式,就是脚本本身 root 不在乎具体几点执行的日常维护

几个要点:

  • 用户名字段是最常见的错误来源。 把 crontab -e 里的写法原样贴进 /etc/crontab 或 /etc/cron.d,命令的第一个词会被当成用户名;反过来,把带用户名的行贴进 crontab -e,用户名会被当成命令去执行。
  • 自己的系统级任务放 /etc/cron.d,不要改 /etc/crontab。 /etc/crontab 属于系统软件包的配置文件,升级时可能被提示覆盖;每个任务一个独立文件,也方便用配置管理工具下发和删除。
  • Debian 系对文件名有要求。 /etc/cron.d 下的文件名,以及 cron.daily 等目录里的脚本名,只能由字母、数字、下划线和连字符组成,带点的会被忽略。最常见的坑是把 cleanup.sh 放进 /etc/cron.daily,它永远不会执行;用 run-parts --test /etc/cron.daily 可以列出会被执行的脚本。/etc/cron.d 下的文件还要属于 root,且不能被其他用户写入。
  • cron.daily 的执行时间不固定。 Debian 系由 /etc/crontab 在每天早上调度,装了 anacron 时交给 anacron;RHEL 系由 anacron 按 /etc/anacrontab 执行,默认只在 3 点到 22 点之间运行并加随机延迟,通常在凌晨 3 点多。需要精确时间的任务,写成 /etc/cron.d 里的具体时间。

一个 /etc/cron.d 文件的例子:

# /etc/cron.d/app-maint (文件名不要带点)
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=""
# 分 时 日 月 周 用户 命令
*/10 * * * * www-data /usr/local/bin/queue-check.sh >/dev/null 2>&1
15 3 * * *   root     /usr/local/bin/mysql-backup.sh >> /var/log/mysql-backup.log 2>&1

写完执行 chmod 644 /etc/cron.d/app-maint,cron 会自动读取,不用重启服务。文件的最后一行要以换行结束,否则最后一个任务可能不会被识别。

运维常用的定时任务配置示例

日志切割

系统自带的 logrotate 每天会运行一次(由 cron.daily 或 systemd 的 logrotate.timer 触发),自己的应用日志建议在 /etc/logrotate.d/ 下写一份配置交给它。没有 logrotate 配置、又想按日期保存的 Nginx 访问日志,可以用 crontab 直接切割:

# 每天 00:00 把访问日志按昨天的日期改名,再让 Nginx 重新打开日志文件
0 0 * * * mv /var/log/nginx/access.log /var/log/nginx/access-$(date -d yesterday +\%F).log && kill -USR1 $(cat /run/nginx.pid)

USR1 信号让 Nginx 重新打开日志文件,不中断服务;pid 文件路径以 nginx.conf 里的 pid 配置为准。系统已经用 logrotate 管理 Nginx 日志时,不要再加这一条,两边会重复切割。

数据库备份

复杂的逻辑写进脚本,crontab 里只留一行调用,脚本里的 % 也就不用转义了:

#!/bin/bash
# /usr/local/bin/mysql-backup.sh
set -euo pipefail
DIR=/data/backup/mysql
mkdir -p "$DIR"
OUT="$DIR/all-$(date +%F).sql.gz"
mysqldump --defaults-extra-file=/root/.backup.cnf --single-transaction --routines --all-databases \
  | gzip > "$OUT.tmp"
mv "$OUT.tmp" "$OUT"
find "$DIR" -name 'all-*.sql.gz' -mtime +7 -delete
# crontab -e 中加入:每天 02:30 执行,输出追加到日志
30 2 * * * /usr/local/bin/mysql-backup.sh >> /var/log/mysql-backup.log 2>&1

set -o pipefail 让 mysqldump 失败时整条管道返回失败,配合 set -e 脚本立即退出:残缺的结果只留在 .tmp 文件里,不会被改名成正式备份,后面清理旧备份的 find 也不会执行;数据库账号写在权限为 600 的 /root/.backup.cnf 里,不要写进脚本或 crontab。find -mtime +7 按整天取整计算,删除的是至少 8 天前修改的文件。

清理临时文件、防止任务重叠、磁盘检查

# 每天 04:00 删除 /tmp/app 下超过 3 天未修改的文件
0 4 * * * find /tmp/app -type f -mtime +3 -delete
# 每 5 分钟同步一次;上一轮还没跑完时,这一轮直接跳过
*/5 * * * * flock -n /run/lock/sync.lock /usr/local/bin/sync.sh >> /var/log/sync.log 2>&1
# 每 30 分钟检查根分区,使用率超过 85% 时写一条系统日志,便于监控采集
*/30 * * * * df -P / | awk 'NR==2 && $5+0 > 85 {print "root usage " $5}' | logger -t disk-check

flock 这一条很实用:同步、采集这类任务偶尔会因为网络慢而超时,如果不加锁,下一轮照常启动,几轮叠加下来会把机器拖垮。

crontab 不执行怎么排查

现象 原因 解决
手动执行正常,cron 里报 command not found cron 的 PATH 只有 /usr/bin:/bin 等少数目录,也不读 .bashrc、.bash_profile 命令写绝对路径,或在任务表顶部写 PATH=...
命令被截断、行为异常 % 在 crontab 里会被换成换行,后面的内容变成命令的标准输入 写成 \%,或者把命令放进脚本
任务好像没执行,也看不到任何输出 输出默认以邮件发给用户,本机没有邮件服务时就丢了 命令末尾重定向 >> 日志文件 2>&1
脚本报 /bin/bash^M: bad interpreter 脚本在 Windows 上编辑过,带 CRLF 换行 sed -i 's/\r$//' 脚本名,并确认有执行权限
/etc/cron.d 里的任务不执行 缺用户名字段、文件名带点(Debian 系)、权限不对、最后一行没有换行 按上一节的要求逐项检查
执行时间差了 8 小时 改时区后没有重启 cron 服务,仍按旧时区 重启 cron 服务

排查用到的命令:

systemctl status cron                 # Debian、Ubuntu;RHEL 系把 cron 换成 crond
grep CRON /var/log/syslog             # Debian、Ubuntu 的执行记录;没有 syslog 文件时用 journalctl -u cron
tail -f /var/log/cron                 # RHEL 系的执行记录
# 用接近 cron 的精简环境手动跑一遍,能复现大部分环境变量问题
env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=/root LOGNAME=root /bin/sh -c '/usr/local/bin/mysql-backup.sh'

日志里出现 CMD (/usr/local/bin/mysql-backup.sh),说明 cron 已经按时启动了命令,问题在命令本身,去看它自己的输出日志;连 CMD 记录都没有,说明时间写错了、任务表没装上,或者 cron 服务没在运行。

在管理系统里怎么落地

crontab 平时很少有人去看,入侵者却常在里面写一条定时下载脚本的任务来维持控制;人员交接时,也常常没人说得清某条任务是谁加的、做什么用。所以修改定时任务这类操作,要放在留痕的通道里完成。Toplink DCIM 的 IPMI 远程管理提供浏览器里的 SSH 控制台,每次会话自动录像,SSH 会话还记录命令级日志,谁在什么时候执行了 crontab -e、改了什么,事后可以回放核对。定时任务把服务器拖垮、SSH 登不上去时,同一页面的 VNC 控制台走 BMC 带外通道,可以登录本地终端把出问题的任务注释掉。

常见问题

crontab 能精确到秒吗?

不能,最小单位是分钟。要每 30 秒执行一次,可以写两行:* * * * * /usr/local/bin/job.sh 和 * * * * * sleep 30; /usr/local/bin/job.sh。更频繁、或者对时间精度有要求的任务,改用 systemd timer(OnUnitActiveSec 配合 AccuracySec)或常驻进程。

crontab -r 误删了全部任务,还能恢复吗?

crontab 没有回收站,只能从备份恢复。没有备份时可以从 cron 日志里找线索:Debian 系用 grep CRON /var/log/syslog 或 journalctl -u cron,RHEL 系看 /var/log/cron,CMD 记录列出了最近执行过的命令和用户,再按执行时间反推时间字段。以后修改前先用 crontab -l 备份一份。

服务器改了时区,crontab 按哪个时间执行?

cron 按 cron 服务进程的时区判断时间。用 timedatectl set-timezone Asia/Shanghai 改完时区后,重启 cron 服务(RHEL 系是 crond,Debian 系是 cron),新时区才会生效。RHEL 系的 cronie 还支持在任务表里写 CRON_TZ= 为这张表单独指定时区。

crontab 和 systemd timer 怎么选?

简单的周期任务用 crontab 足够。需要依赖其他服务、限制 CPU 和内存、错过执行时间后开机补跑(Persistent=true)、日志统一进 journal 时,用 systemd timer 更合适。两者可以并存,但同一个任务只在一处配置,避免重复执行。

想看看在你的机房里怎么用?

从一次产品演示开始,梳理你的业务链路。

查看价格
服务热线 400-112-2951

让我们聊聊你的 IDC 业务

扫码添加企业微信,预约产品演示或申请试用。

Toplink 企业微信二维码

长按保存或使用企业微信 / 微信扫描

也可致电
400-112-2951
Telegram
@TopLink88