Linux 定时任务

Administrator 2

Linux 定时任务


目录

  1. 软件包安装
  2. cron:周期性定时任务
  3. crontab 的格式与语法
  4. 系统级定时任务:/etc/crontab 与 /etc/cron.d
  5. cron 服务、日志与排错
  6. at:一次性定时任务
  7. sleep + &:简单的一次性延迟
  8. systemd timer:现代替代方案
  9. anacron:处理关机导致的漏执行
  10. crontab 环境变量坑:脚本里 PATH 不完整
  11. 实战:完整的定时备份示例
  12. 常见问题排查清单
  13. 总结与对比
  14. 参考资料

软件包安装

Rocky/RHEL 的定时任务工具不一定默认安装(尤其 Minimal 安装),使用前先确认并安装。

相关软件包

提供 作用
cronie crondcrontab 主周期性定时任务服务与工具
cronie-anacron anacron 支持错过时间的补跑(可选)
at atatqatrm 一次性计划任务

检查是否已安装

which crontab at       # 看命令是否存在
systemctl status crond  # 看 crond 服务是否在运行
rpm -q cronie at       # 查看包是否安装

安装命令

# 安装 cronie(含 crontab 和 crond 服务)
sudo dnf install -y cronie

# 安装 at(一次性任务)
sudo dnf install -y at

# 启动并设置开机自启
sudo systemctl enable --now crond
sudo systemctl enable --now atd

其他发行版:Ubuntu/Debian 用 sudo apt install cronsudo apt install at,服务名是 cron

安装后可用 systemctl status crond 确认服务正常。


一、cron:周期性定时任务

1.1 cron 是什么

cron 是一个守护进程(daemon),负责在约定的时间点自动执行用户定义的任务。它对应的服务名在大多数发行版中是 crond。相关的命令与文件包括:

命令 / 文件 作用
crond cron 守护进程(服务)
crontab 用户管理自己的定时任务
/etc/crontab 系统级定时任务(注意:带用户字段,格式与用户 crontab 不同
/etc/cron.d/ 系统级定时任务的扩展目录(每个文件格式类似 /etc/crontab
/etc/cron.hourly/cron.daily/cron.weekly/cron.monthly/ 按周期执行的脚本目录

1.2 crontab 基本管理命令

crontab -l            # 查看当前用户的定时任务
crontab -e            # 编辑当前用户的定时任务(会用默认编辑器)
crontab -r            # 删除当前用户的全部定时任务
crontab -u username   # 指定操作某用户的定时任务(需 root)
crontab file          # 用文件内容覆盖当前用户的定时任务
  • 每个用户有独立的 crontab,文件存在 /var/spool/cron/
  • crontab -e 保存后立即生效,无需重启服务。
  • crontab file 可以从文件批量导入任务,也常用于脚本化配置(见实战章节)。

参考:man 1 crontab菜鸟教程 - Linux crontab


二、crontab 的格式与语法

2.1 5 个时间字段 + 命令

每行一条任务,格式为:

分  时  日  月  周     命令
*   *   *   *   *    /path/to/command
字段 含义 取值范围
分(minute) 一小时内的第几分钟 0–59
时(hour) 一天中的第几小时 0–23
日(day of month) 一个月中的第几天 1–31
月(month) 一年中的第几个月 1–12
周(day of week) 一周中的第几天 0–7(0 和 7 都代表星期日)

2.2 关键语法元素

字符 含义 示例
* 任意值(每分钟 / 每小时…) * * * * * 每分钟
, 列举多个值 1,15,30 表示第 1、15、30 分钟
- 区间 9-17 表示 9 点到 17 点
/ 步长 */5 每 5 分钟;0-30/10 在 0–30 分钟内每 10 分钟

2.3 日与周是"或"的关系

当"日"和"周"同时指定(都不为 *)时,两个条件满足其一即可执行(OR 关系)。这是 cron 最容易搞错的地方:

0 0 1 * 1  command     # 只要"每月 1 日"或"周一"任一满足就执行

也就是说,上例会同时覆盖"每月 1 号"和"每周一"。若你真正想要的是"每月 1 日正好是周一",用单一时间字段无法精确表达,通常改用脚本内判断(如 date +%ddate +%u 同时判断),或拆成更细的逻辑。

参考 man 5 crontab 原文:"If both fields are restricted (i.e., aren't *), the command will be run when either field matches the current time."

2.4 常见例子

# 每天早上 6 点执行备份脚本
0 6 * * * /usr/local/bin/backup.sh

# 每 5 分钟执行一次
*/5 * * * * /usr/local/bin/healthcheck.sh

# 每周一、周三、周五的 8 点和 20 点执行
0 8,20 * * 1,3,5 /usr/local/bin/report.sh

# 每月 1 号凌晨 1 点执行
0 1 1 * * /usr/local/bin/monthly.sh

# 每个工作日上午 9:30 执行
30 9 * * 1-5 /usr/local/bin/workday.sh

# 每天 3:00 进目录跑清理脚本
0 3 * * * cd /tmp && ./clean.sh

命令最好写绝对路径(如 /usr/bin/find 而非 find),因为 cron 环境的 PATH 有限(见第九章的坑)。

参考:man 5 crontab菜鸟教程 - Linux crontab


三、系统级定时任务:/etc/crontab 与 /etc/cron.d

3.1 /etc/crontab

系统级 crontab 与用户 crontab 的格式不同:它多了一个用户字段(在时间之后、命令之前,指定以哪个用户身份运行):

分 时 日 月 周  用户   命令
0 2 * * *      root   /usr/local/bin/cleanup.sh
  • 只有 root 能编辑 /etc/crontabsudo vim /etc/crontab)。
  • 由于带用户字段,可以用来以任意用户身份调度任务。

3.2 /etc/cron.d/

/etc/cron.d/ 目录里的每个文件格式与 /etc/crontab 相同(也带用户字段),适合按软件/模块拆分系统级任务。例如:

# /etc/cron.d/myapp
30 3 * * *  root   /usr/local/bin/myapp-maintenance.sh
  • 包管理器(rpm/dnf)安装的软件常把自己的定时任务放进 /etc/cron.d/

3.3 /etc/cron.hourly/、cron.daily/ 等周期目录

系统还提供按周期执行的脚本目录,把脚本(需可执行权限)放进对应目录即可:

目录 执行周期
/etc/cron.hourly/ 每小时
/etc/cron.daily/ 每天
/etc/cron.weekly/ 每周
/etc/cron.monthly/ 每月

这些目录通常由 anacron 驱动,能处理"关机导致的漏执行"(见第八章)。放入脚本时记得 chmod +x 并写好 shebang(#!/bin/bash)。


四、cron 服务、日志与排错

4.1 服务管理

systemctl status crond          # 查看服务状态(systemd 系统)
systemctl enable --now crond    # 开机自启并立即启动

4.2 查看 cron 日志

定时任务执行情况会写入日志,排查"任务没跑 / 报错"主要靠它:

# 传统日志(RHEL/CentOS/Rocky)
sudo tail -f /var/log/cron

# Debian/Ubuntu(旧版 rsyslog)
grep CRON /var/log/syslog

# 用 systemd 看 crond 服务日志
sudo journalctl -u crond --since today
  • 日志会记录每次任务的执行时间命令,以及脚本的输出。
  • 若任务没执行,先看这里是否有调度记录。

4.3 常见排错

# 1. 任务是否被调度:看日志
sudo tail -50 /var/log/cron

# 2. 服务是否在运行
systemctl status crond

# 3. crontab 语法是否正确
crontab -l    # 检查语法,若语法错会提示

# 4. 手动执行脚本测试
bash /path/to/script.sh    # 看是否报错

# 5. 看脚本输出(cron 会把输出发邮件或丢弃)
# 建议在脚本里把输出重定向到文件
* * * * *  /path/script.sh >> /var/log/myscript.log 2>&1

排错思路:任务没跑,先看 /var/log/cron 有没有调度记录;有记录但脚本没效果,多半是脚本本身或环境变量问题(见第九章)。


五、at:一次性定时任务

at 用于安排只执行一次的未来任务,适合"5 分钟后再做某事""今晚 11 点关服务器"这类场景,与 cron 的周期任务不同。

5.1 基本用法

at 22:00                    # 交互式输入命令,Ctrl+D 结束
at now + 5 minutes          # 5 分钟后
at 09:00 tomorrow           # 明天 9 点
at 12:00 2026-01-01         # 指定具体时间
at midnight / at noon       # 午夜 / 正午

通过管道或文件喂给 at 也常用:

echo "systemctl stop myservice" | at now + 10 minutes
at 22:00 < script.sh        # 通过文件喂给 at

5.2 管理命令

atq                 # 查看待执行的一次性任务列表(队列)
atrm <jobid>        # 删除指定任务
  • atq 列出待执行任务(含编号),atrm 按编号删除。
  • 输出会通过邮件发给用户(若配置了邮件),或写入日志。
  • 服务是 atd,需 systemctl start atd(安装章节已 enable --now)。

参考:man 1 atman 1 atqman 1 atrm菜鸟教程 - Linux at 命令


六、sleep + &:简单的一次性延迟

不想装 at、只是"延迟 X 秒后做一次",可以用后台方式:

(sleep 300; /path/to/task.sh) &
  • 适合临时、一次性的简单延迟。
  • 缺点:依赖当前 shell 存活,进程挂了任务就没了,只适合临时使用。

七、systemd timer:现代替代方案

很多新系统推荐用 systemd timer 取代 cron,因为它有更强的依赖管理、日志、随机延迟、失败重试等能力。

7.1 需要两个文件

任务单元 foo.service(描述要做什么):

[Unit]
Description=My periodic task

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh

定时器单元 foo.timer(描述何时触发):

[Unit]
Description=Run foo every hour

[Timer]
OnCalendar=*-*-* 00:01:00   # 每天零点一分
OnBootSec=10min             # 开机后 10 分钟(可选)
RandomizedDelaySec=2min     # 随机延迟,避免负载尖峰
Persistent=true             # 错过后补执行

[Install]
WantedBy=timers.target

7.2 启用与查看

sudo systemctl start foo.timer
sudo systemctl enable foo.timer
systemctl list-timers        # 查看所有定时器及下次触发时间
journalctl -u foo.service    # 查看任务执行日志

7.3 OnCalendar 语法

OnCalendar 语法与 cron 思路类似但更灵活,常用写法:

  • Mon..Fri *-*-* 09:00:00:工作日早 9 点
  • *:0/15:每 15 分钟
  • dailyhourlyweekly:关键字

参考:man 5 systemd.timerman 7 systemd.time


八、anacron:处理关机导致的漏执行

cron 在关机期间错过的任务不会补跑anacron 专为解决这一点:开机后检查上次执行时间,如果错过了就补执行。它用于日 / 周 / 月这类低频任务(不处理分钟级)。

  • 配置在 /etc/anacrontab
  • 系统里的 /etc/cron.daily/etc/cron.weekly/etc/cron.monthly 等目录,通常就是由 anacron 驱动补跑的(由 cronie-anacron 包提供,见安装章节)。

九、crontab 环境变量坑:脚本里 PATH 不完整

这是定时任务最常见的坑:脚本在终端里手动运行正常,但 cron 定时跑却报"命令找不到"(command not found)。

9.1 为什么

  • 你在终端登录时,shell 会加载 ~/.bashrc/etc/profile 等,PATH 很完整(包含 /usr/local/bin、/usr/bin 等)。
  • cron 执行脚本时不会加载这些登录环境,PATH 往往只有很有限的值(如 /usr/bin:/bin)。
  • 于是脚本里写 mysqldumpdocker非标准路径命令时,cron 里就"找不到"。

9.2 复现与验证

# 在 crontab 里临时加一行,看 cron 环境的 PATH
* * * * *  echo "PATH=$PATH" >> /tmp/cron_env.txt

# 等 1 分钟后查看
cat /tmp/cron_env.txt
# 对比终端里的 PATH
echo $PATH
  • 你会看到 cron 环境里的 PATH 明显比终端短。

9.3 解决办法

方法一:脚本开头显式设置 PATH(推荐)

#!/bin/bash
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:$PATH
# 脚本其他内容

方法二:脚本里用命令的绝对路径

#!/bin/bash
/usr/bin/mysqldump ...

方法三:在 crontab 里定义 PATH / 其他环境变量(对整个 crontab 生效)

# crontab 顶部(在任务行之前)定义
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=admin@example.com

0 2 * * * /usr/local/bin/backup.sh > /tmp/backup.log 2>&1
  • MAILTO 用于设置 cron 输出邮件的收件人;不设置则默认发往本用户邮箱(堆积在 /var/spool/mail)。

推荐组合:脚本里用绝对路径或开头 export PATH=...,同时把脚本输出重定向到日志文件,方便排错。

参考:crontab 常见坑(PATH)


十、实战:完整的定时备份示例

10.1 编写备份脚本

# 编写脚本 /usr/local/bin/daily_backup.sh
sudo cat > /usr/local/bin/daily_backup.sh <<'EOF'
#!/bin/bash
# 每日备份:备份 /var/www,并清理 7 天前的旧备份
export PATH=/usr/local/bin:/usr/bin:/bin
STAMP=$(date +%Y%m%d_%H%M%S)
tar -czf /backup/www_$STAMP.tar.gz -C /var www
find /backup -name 'www_*.tar.gz' -mtime +7 -delete
EOF

# 给脚本可执行权限
sudo chmod +x /usr/local/bin/daily_backup.sh

# 手动执行一次测试(务必先测通再配 crontab)
sudo /usr/local/bin/daily_backup.sh
ls -lh /backup/    # 确认备份文件生成

10.2 追加式写入 crontab

crontab file 可以从文件导入。这里用"追加式"写法,把新任务加进现有任务而不覆盖:

# 追加一行,每天凌晨 2:30 执行,输出写日志
(crontab -l 2>/dev/null; echo "30 2 * * * /usr/local/bin/daily_backup.sh >> /var/log/backup.log 2>&1") | crontab -

crontab -l   # 验证
  • crontab -l 2>/dev/null 取出当前任务(没有则忽略报错)。
  • echo 追加新任务行,两者一起通过管道写入 crontab -(从标准输入导入)。

10.3 验证定时任务生效

crontab -l                    # 确认任务已保存
sudo tail -f /var/log/cron    # 看是否按计划执行
ls -lh /backup/               # 确认备份文件生成

脚本要点:开头 export PATH 解决环境变量坑;输出重定向到日志便于排查;用 -mtime +N 清理旧文件防止磁盘占满。

参考:Rocky Linux - Automating processes with cron and crontab


十一、常见问题排查清单

  1. 任务没执行 → 检查服务运行 systemctl status crond;看日志 /var/log/cron
  2. 执行了但没效果 → 多半是环境问题(PATH、绝对路径、权限)。
  3. 脚本没执行权限chmod +x script.sh
  4. 时间配错 → 日/周是"或"关系,容易误判。
  5. 输出卡住 → cron 用邮件发输出,可能堆积在 /var/spool/mail
  6. 中文 / 时区 → cron 用的是服务器本地时区,注意 date 输出是否与预期一致。

十二、总结与对比

12.1 核心速查

需求 命令
安装 sudo dnf install -y cronie at
编辑当前用户任务 crontab -e
列出任务 crontab -l
删除任务 crontab -r
查看 cron 日志 sudo tail -f /var/log/cron
一次性任务 echo 命令 | at 时间
查看 at 任务 atq
删除 at 任务 atrm 编号
服务状态 systemctl status crond

12.2 工具选择对比

需求 工具
周期性(分钟 / 小时 / 天 / 周 / 月) cron / crontab
一次性、指定未来某个时刻 at
低频且要补跑错过的任务 anacron
需要依赖控制、日志、随机延迟、现代系统 systemd timer
简单临时延迟 sleep + &

12.3 动手练习建议

# 1. 安装
sudo dnf install -y cronie at
sudo systemctl enable --now crond atd

# 2. 加一个测试任务(每分钟写一行到文件)
crontab -e
# 加:* * * * *  echo hello >> /tmp/cron_test.txt
sleep 65; cat /tmp/cron_test.txt   # 应能看到内容

# 3. 一次性任务
echo "echo at-test > /tmp/at_test.txt" | at now + 1 minute
atq

# 4. 体验 PATH 坑
# crontab 加:* * * * *  echo PATH=$PATH > /tmp/cron_path.txt
# 对比终端 echo $PATH

提示:练习在虚拟机进行。crontab 改错可能导致任务异常,注意先备份(crontab -l > backup.txt)。


参考资料