Linux cron 基础:crontab 语法详解、环境变量与日志配置

说实话,很多运维新手第一次接触 cron 都觉得它挺简单——不就是一行「分时日月周」再加个命令吗?我一开始也这么想,直到有一次线上脚本没跑,排查了半天才发现是环境变量的问题。嗯,这章咱们就把 cron 的底裤扒干净。

crontab 语法:五颗星加上命令

标准 crontab 行格式长这样:

分 时 日 月 周  命令(路径)
0  2  *  *  *  /usr/local/bin/check_disk.sh

五个字段用空格隔开。每个字段的取值范围:

字段范围特殊符号
分 (minute)0‑59*, /, -, ,
时 (hour)0‑23*, /, -, ,
日 (day of month)1‑31*, /, -, ,
月 (month)1‑12*, /, -, ,
周 (day of week)0‑7 (0和7都代表周日)*, /, -, ,

几个常用组合,我个人习惯记成「口诀」:

  • 0 2 * * * – 每天凌晨2点整
  • */5 * * * * – 每5分钟一次
  • 0 8-17 * * 1-5 – 工作日早8点到晚5点,每小时整点
  • 0 0 1 * * – 每月1号零点
一个小技巧:周和日同时设置时,是「或」的关系。比如 0 0 15 * 5 表示每月15号 或 每周五零点执行,并不是同时满足。

环境变量:cron 最大的坑

我踩过最深的坑就是这个。cron 任务执行时,不会加载你手动 source 的 .bashrc 或 .bash_profile。它只继承非常基础的 PATH(通常是 /usr/bin:/bin),连 /usr/local/bin 都没有。你想想看,脚本里用的命令 docker、python3、aws 如果在那个 PATH 里找不到,cron 直接报错,而你连日志都看不到——这才是最气人的。

建议在 crontab 文件顶部显式设置环境变量:

SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
MAILTO=admin@example.com

0 3 * * * /opt/scripts/backup.sh

记住了:SHELL、PATH、MAILTO 这三个变量,写在 crontab 最前面。我用这个方式后,再没因为环境变量出过问题。

避坑:我曾经接过一个工单,脚本手动执行完美,cron 里就不行。排查两小时,最后发现脚本里用了 ~ 波浪号。cron 默认 shell 是 /bin/sh,很多发行版下 sh 不展开波浪号。改成 $HOME 就好了。

日志配置:看不到的故障等于没发生

默认 cron 日志在 /var/log/cron(RHEL/CentOS)或 /var/log/syslog(Debian/Ubuntu),但这些日志记录了所有 cron 调度事件,并不会把脚本的输出单独归档。

我建议的做法:在脚本内部重定向输出,或者直接在 crontab 行里追加:

0 2 * * * /opt/scripts/healthcheck.sh >> /var/log/healthcheck.log 2>&1

这样每次执行的标准输出和错误输出都追加到同一个文件。怕日志文件太大?可以配合 logrotate 做轮转。你看,一条命令就把审计和排错都解决了。

核心原则:永远不要把脚本的 stdout/stderr 丢进黑洞(>/dev/null 2>&1)除非你确定永远不会出错。我见过太多人为了省事直接丢弃输出,出问题了连根毛都查不到。

一张图理清 cron 执行全景

下面这张 SVG 图把你刚才学的三个部分串起来:从编辑 crontab 到环境变量加载,再到日志落地。

cron 执行全过程 crontab -e 编辑 crond 守护进程 时间匹配? 否 等待下一分钟 是 加载 crontab 环境变量 执行命令 + 日志写入 环境变量注意点 • PATH 需包含脚本所用命令 • SHELL 建议设为 /bin/bash • MAILTO 接收错误通知

从图里你能看到:编辑完 crontab 后,crond 守护进程每分钟检查时间是否匹配。匹配后第一步就是加载你在 crontab 中定义的环境变量(SHELL、PATH、MAILTO),然后真正的脚本才开始执行。最后输出被重定向到日志文件。每一步你都清楚,排查时才不会抓瞎。

几个实用工具与调试技巧

  • crontab -l 列出当前用户的定时任务。注意:如果没任务,它会报 no crontab for xxx,别慌。
  • crontab -r 删除全部任务。我建议先 crontab -l > /tmp/mycron.bak 备份一下再删。
  • 测试环境变量: 写一个 env > /tmp/cron_env.log 的测试任务,看看实际执行时的环境变量列表。我在新服务器上总先跑这条确认。
  • 查看最近一次执行时间: 在日志文件里 grep "cron" /var/log/syslog | grep 你的脚本名,能看到启动时间和 PID。
再次强调:永远不要在生产环境使用 crontab -r 而不备份。我见过同事手滑,把整个服务器的定时任务全清空,幸好有 Git 仓库回滚。嗯,从那以后我的服务器 crontab 都会版本控制。