教程

Crontab:Linux 任务计划示例、语法与常见错误

更新于: · Skillaria.Top

Crontab 是 cron 守护进程运行命令所依据的计划表:每一行由五个时间字段(分、时、日、月、周)和命令组成,例如 */5 * * * * /opt/backup.sh 每 5 分钟运行一次脚本。使用 crontab -e 编辑该表;大多数问题都源于 cron 的环境很精简:请使用完整路径,并把输出重定向到日志。

cron 是 Linux 中标准的任务调度器:守护进程 cron 每分钟读取一次计划表(crontab),并运行到时间的命令。夜间备份、清理临时文件、更新证书、发送报告,都是这样完成的。下面是语法、常见情形的 crontab 示例,以及导致任务“悄无声息”地不执行的原因分析。

如何打开和编辑 crontab

每个用户都有自己的计划表。通过 crontab 命令操作,而不是直接编辑 /var/spool/cron 中的文件:

命令作用
crontab -e用编辑器打开自己的计划表(首次运行时会让你选择编辑器)
crontab -l显示当前的计划表
crontab -r直接删除整个计划表,不予确认
crontab -i -r删除前要求确认(Debian/Ubuntu、RHEL)
sudo crontab -u alice -e编辑其他用户的计划表
crontab 文件用文件内容替换计划表

保存 crontab -e 后,它会检查语法并显示 crontab: installing new crontab。如果某行有错误,你会看到 errors in crontab file, can't install,并被提示重新编辑。无需重启守护进程——更改会自动生效。

注意 -r:字母 e 和 r 在键盘上相邻,而且没有备份。一个好习惯是保留备份:

crontab -l > ~/crontab.bak
# 恢复
crontab ~/crontab.bak

crontab 语法:五个时间字段

每条任务的格式如下:

# ┌───────── 分钟 (0–59)
# │ ┌─────── 小时 (0–23)
# │ │ ┌───── 日期 (1–31)
# │ │ │ ┌─── 月份 (1–12 或 jan–dec)
# │ │ │ │ ┌─ 星期 (0–7 或 sun–sat;0 和 7 都表示星期日)
# │ │ │ │ │
  * * * * * 命令

每个字段中可以使用以下特殊符号:

符号含义示例
*任意值* * * * *——每分钟
,列表0 9,18 * * *——9:00 和 18:00
-范围0 9-18 * * *——9 点到 18 点之间每小时一次
/步长*/15 * * * *——每 15 分钟

步长可以与范围组合:0 8-20/2 * * *——8 点到 20 点之间每两小时一次(8、10、…、20)。时间按系统的时区计算,可用 timedatectl 查看。

每天使用的 crontab 示例

计划crontab 行
每分钟* * * * *
每 5 分钟*/5 * * * *
每 30 分钟*/30 * * * * 或 0,30 * * * *
每小时整点0 * * * *
每 6 小时0 */6 * * *
每天 3:000 3 * * *
每天 23:4545 23 * * *
工作日 9:000 9 * * 1-5
周末中午0 12 * * 6,0
每周一 8:3030 8 * * 1
每月 1 日 0:000 0 1 * *
每季度0 0 1 1,4,7,10 *
每年 1 月 1 日0 0 1 1 *
工作时间内每 10 分钟*/10 9-18 * * 1-5

完整的任务行由计划加上命令组成:

# 每晚 3:00 备份数据库
0 3 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1

# 每周日 4:00 删除 7 天以前的临时文件
0 4 * * 0 /usr/bin/find /tmp/app -type f -mtime +7 -delete

# 每 5 分钟检查一次网站
*/5 * * * * /usr/bin/curl -fsS https://example.com/health > /dev/null || /usr/local/bin/alert.sh

@reboot、@daily 等特殊字符串

可以用简短的别名代替五个字段:

别名等效写法
@rebootcron 守护进程启动时运行一次(系统开机之后)
@hourly0 * * * *
@daily / @midnight0 0 * * *
@weekly0 0 * * 0
@monthly0 0 1 * *
@yearly / @annually0 0 1 1 *
@reboot /home/alice/bin/start-tunnel.sh >> /home/alice/tunnel.log 2>&1

对于需要持续运行并在崩溃后自动重启的服务,@reboot 并不可靠:更好的做法是编写一个带有 Restart=on-failure 的 systemd 单元。

如何每隔 N 秒运行一次任务

cron 的最小单位是一分钟。如果需要更频繁地运行,可以用两种方法。第一种是使用 sleep 错开多行:

* * * * * /opt/check.sh
* * * * * sleep 30; /opt/check.sh

第二种更为规范:使用带有 OnUnitActiveSec=30s 的 systemd 定时器。如果任务需要每分钟运行多次,通常更适合由常驻守护进程或定时器完成,而不是 cron。

系统 crontab:/etc/crontab 与 /etc/cron.d

除了用户的计划表,系统中还有系统级的计划表。它们的格式有所不同:在时间与命令之间,需要写上运行命令所用的用户名。

# /etc/cron.d/certbot-renew
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

0 */12 * * * root certbot renew --quiet

各类文件的作用:

  • /etc/crontab——主系统计划表;在 Debian/Ubuntu 中,下面的目录就是由它来触发的。
  • /etc/cron.d/——独立的任务文件,适合由软件包安装或通过 Ansible 部署。
  • /etc/cron.hourly/、cron.daily/、cron.weekly/、cron.monthly/——存放没有时间表的可执行脚本,由 run-parts(或 anacron)运行。

run-parts 的一个陷阱:在 Debian 和 Ubuntu 中,它会跳过文件名中含有点号的文件。把 backup.sh 放进 /etc/cron.daily/ 将不会运行——请将其命名为 backup。要查看将会执行哪些文件,可运行 run-parts --test /etc/cron.daily。

/etc/cron.d/ 中的文件必须属于 root,不能对组和其他用户可写,并且要以换行符结尾——否则 cron 会忽略它们。

cron 的环境:PATH、SHELL 与变量

“终端里能运行,cron 里却不行”的主要原因就是环境不同。cron 通过 /bin/sh 运行命令,不会读取你的 .bashrc 和 .profile,并且 PATH 非常精简(通常是 /usr/bin:/bin)。因此,位于 /usr/local/bin 中的 docker、node 或 php 可能根本找不到。

可以在计划表的开头设置变量:

SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com
CRON_TZ=Europe/Moscow

0 6 * * * cd /srv/app && ./report.sh
  • MAILTO——任务输出发送到哪个邮箱;MAILTO="" 会关闭邮件发送。
  • CRON_TZ——计划所用的时区(RHEL/Fedora 的 cronie 支持;Debian 版的 cron 不支持此变量)。
  • 变量值中不支持 $HOME/bin 之类的替换——请完整写出路径。

最可靠的做法是在任务中为所有程序和文件使用绝对路径,并在脚本的第一行写上 shebang:#!/bin/bash。

在哪里查看 cron 日志

cron 会记录任务的启动事件,但不记录任务的输出:

# Debian/Ubuntu
grep CRON /var/log/syslog
# 通过 systemd-journald
journalctl -u cron --since today      # Debian/Ubuntu
journalctl -u crond --since today     # RHEL、Fedora、Rocky
# RHEL 且没有 journald
tail -f /var/log/cron

启动记录的样子大致如下:

Oct 14 03:00:01 web1 CRON[21544]: (root) CMD (/usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1)

要查看任务打印了什么、以及因何错误失败,请把标准输出和标准错误都重定向到文件:>> /var/log/job.log 2>&1。否则输出会通过本地邮件发送;如果没有安装邮件代理,输出就会丢失(日志中会出现 No MTA installed, discarding output)。

为什么 cron 不运行任务:常见错误

找不到命令

症状:任务日志中出现 command not found。原因是 PATH 过于精简。解决办法:使用程序的完整路径(which docker 可以告诉你路径),或者在计划表开头设置 PATH=。

命令中的百分号

在 crontab 中,% 表示换行:第一个 % 之后的所有内容都会作为标准输入传给命令。因此,常见的把日期写进文件名的行会出错:

# 错误——命令会在第一个 % 处被截断
0 2 * * * tar czf /backup/site-$(date +%F).tar.gz /var/www
# 正确——进行转义
0 2 * * * tar czf /backup/site-$(date +\%F).tar.gz /var/www

更简单的办法是把逻辑放进脚本中,那样就不需要转义 %。

脚本不可执行,或换行符错误

Permission denied 表示文件没有 x 执行权限:执行 chmod +x script.sh,或者显式运行 /bin/bash /opt/script.sh。如果脚本是在 Windows 中编写的,CRLF 换行符会导致奇怪的错误 /bin/bash^M: bad interpreter——用 sed -i 's/\r$//' script.sh 或 dos2unix 修复。

相对路径与工作目录

任务在用户的主目录中启动。如果脚本读取 ./config.ini,它会在 ~ 中查找该文件。请在命令前加上 cd /srv/app &&,或在脚本内部使用绝对路径。

同时指定日期和星期

如果日期和星期两个字段都有指定值,cron 只要其中任意一个匹配就会运行,而不是两者都要匹配。例如 0 9 13 * 5 会在每月 13 日和每个星期五运行,而不是只在“星期五的 13 日”运行。需要的条件应在命令内部检查:

0 9 13 * * [ "$(date +\%u)" = 5 ] && /opt/friday13.sh

任务相互重叠

如果备份耗时超过了运行间隔,下一次运行会与上一次叠加执行。防护方法是使用 flock 加锁:

*/10 * * * * /usr/bin/flock -n /tmp/sync.lock /opt/sync.sh

加上 -n 参数后,如果第一个实例仍在运行,第二个实例会立即退出。

cron 守护进程没有运行

检查 systemctl status cron(在 RHEL 中为 crond)。在最小化镜像(例如 Docker 容器)中根本没有 cron 守护进程——计划表中的任务也就没有程序去执行了。

如何调试 cron 任务

在尽量接近 cron 的环境中运行命令:

env -i SHELL=/bin/sh PATH=/usr/bin:/bin HOME=$HOME /bin/sh -c '/opt/backup.sh'

如果这里出错,那么 cron 中也会出错。可以暂时把计划改为 * * * * *,将输出重定向到文件,然后用 tail -f 实时查看。在线的 cron 表达式计算器可以帮助检查计划本身,但最终还是要以系统日志为准。

谁可以使用 cron:cron.allow 与 cron.deny

访问权限由文件 /etc/cron.allow 和 /etc/cron.deny 控制。如果存在 cron.allow,则只有其中列出的用户可以使用 crontab。如果没有它,但存在 cron.deny,则除了列出的用户外所有人都可以使用。被禁止的用户运行 crontab -e 时会收到消息 You (bob) are not allowed to use this program。

总结

crontab 就是由五个时间字段和命令组成的行:*/5 * * * *——每 5 分钟,0 3 * * *——每晚,0 9 * * 1-5——工作日。用 crontab -e 编辑计划表,使用绝对路径,转义 %,把输出写入日志,并用 flock 保护耗时较长的任务。掌握这些 crontab 示例,就能应付大多数服务器任务;任务自动化与计划的实际操作会在 Linux 课程中详细讲解。

常见问题

cron 与 anacron 有什么区别?

cron 在精确的时间运行任务,如果计算机在该时间关机,任务就会被跳过。anacron 保证每日、每周和每月的任务在开机后执行,因此适合笔记本和台式机。

cron 和 systemd 定时器哪个更好?

cron 更简单,几乎所有系统都有。systemd 定时器提供 journalctl 日志、依赖关系、资源限制,以及用于补跑错过任务的 Persistent=true 参数,因此在现代服务器上,重要任务越来越多地使用它。

用户的 crontab 实际保存在哪里?

在 Debian 和 Ubuntu 中位于 /var/spool/cron/crontabs/用户名,在 RHEL 和 Fedora 中位于 /var/spool/cron/用户名。不建议直接编辑这些文件,请使用 crontab -e。

如何临时停用 crontab 中的任务?

通过 crontab -e 在该行前面加上 # 进行注释。若要停用用户的全部任务,先用 crontab -l 把计划表保存到文件,然后用 crontab -r 删除它。

如何在每月最后一天运行任务?

没有专门的字段。可以在每月 28–31 日运行,并在命令中检查明天是否为 1 日:0 23 28-31 * * [ "$(date -d tomorrow +\%d)" = 01 ] && /opt/job.sh。

通过视频学习本主题

课程“Linux: с нуля до уверенного администрирования”结合实践详细讲解了这一主题。

前往课程