Linux 定时任务:cron 与 systemd 定时器配置及验证

Linux 中,简单的定时命令可用 cron,需要结合服务状态和日志管理的任务可用 systemd 定时器。配置成功不代表任务成功:应分别检查计划如何被解释、是否实际执行、输出结果和失败状态。

先按Bash 脚本入门直接运行命令并验证结果,再安排定时执行。用户 systemd 管理器在注销后的生命周期及 linger 策略要在实际虚拟机确认,不能仅凭启用了定时器就假定它始终运行。

参考环境为常规 Ubuntu 26.04 LTS 或 Rocky Linux 9 系列虚拟机。容器、WSL 和其他发行版的服务及软件包可能不同;先按虚拟机环境准备(英语)确认版本并准备控制台恢复方式。

按任务需求选择 cron 或定时器

比较项 cron systemd 定时器
配置单位 时间字段加一条命令 timer 与 service 单元
执行验证 命令输出和发行版日志 服务状态与 journal 日志
环境 不同于交互式 Shell 由单元等配置的运行环境
错过计划 传统 cron 通常不补跑 结合 OnCalendar 与 Persistent 判断

软件包与守护进程名称取决于发行版、版本和安装方式,不应按系统名称猜测。Ubuntu 26.04 的官方 cron(8) 页面由 cronie 软件包提供;请在本机检查实际安装包与服务。也要区分传统 cron 和 anacron 等补充工具。下方 systemd 示例要求用户管理器可用,不适用于只有命令行工具、却没有运行管理器的环境。

先读懂 cron 时间字段与环境

crontab -e 编辑当前用户的计划。用户 crontab 的五个时间字段依次是分、时、月中日期、月、星期,随后是命令。下方只是每天 02:15 运行的格式示例,不是可直接在终端执行的命令;必须先创建并手动验证 job.sh,再把 /home/USER 替换为实际绝对路径,确认日志位置可写。

15 2 * * * /bin/bash /home/USER/bin/job.sh >> /home/USER/job.log 2>&1

>> 追加标准输出,2>&1 把标准错误合并到该输出;长期运行还需日志保留和轮转。系统 /etc/crontab 在命令前另有用户字段,不能把两种格式混用。

cron 不会原样继承终端里的 PATH、工作目录和变量。使用绝对路径,在脚本中明确工作目录,避免将秘密值直接写进 crontab。Cronie 中,月中日期和星期同时受限时,匹配其中之一就可能执行,并非要求两者都满足。还需核对所用实现的时区与夏令时规则:不存在的本地时间可能跳过,重复时刻可能重复运行。命令中的未转义 % 还有换行及标准输入的特殊含义,不能当作普通 Shell 字符直接使用。

创建完整的用户服务和定时器

先确认下面两个名字没有被已有单元使用;若冲突,换一组一致的练习名称,并同步修改后续命令,不能覆盖原文件。使用普通账号,先以 command -v date 确认日期工具路径;若不是 /usr/bin/date,调整 ExecStart。这个示例每分钟把时间写到用户日志,不创建 root 服务。

逐步执行,文件创建或日历解析失败就停止。两个 here-document 的 EOF 标记要独占一行,文件用 LF 换行保存。不要在尚未确认用户管理器和文件内容的环境直接执行注册操作。

mkdir -p "$HOME/.config/systemd/user"
cat > "$HOME/.config/systemd/user/linux-course-clock.service" <<'EOF'
[Unit]
Description=Linux course timestamp exercise

[Service]
Type=oneshot
ExecStart=/usr/bin/date --iso-8601=seconds
EOF
cat > "$HOME/.config/systemd/user/linux-course-clock.timer" <<'EOF'
[Unit]
Description=Run the course clock every minute

[Timer]
OnCalendar=*-*-* *:*:00
AccuracySec=1s
Persistent=true

[Install]
WantedBy=timers.target
EOF
systemd-analyze calendar '*-*-* *:*:00'
systemctl --user daemon-reload
systemctl --user enable --now linux-course-clock.timer

相同基本名称的 timer 默认启动对应 service。Type=oneshot 表示这次命令完成后服务可结束;ExecStart 直接执行 date,不是任意 Shell 命令行。OnCalendar=*-*-* *:*:00 指定每分钟的第 0 秒;先看 systemd-analyze 给出的规范表达式和下一次时刻。

AccuracySec=1s 设置可合并触发的时间窗口,不保证硬实时执行。Persistent=true 适用于 OnCalendar 定时器:重新激活时若发现停用期间至少错过一次,就补触发一次,不会把每次错过的任务逐个回放。它不等于机器在关机期间也能执行,时钟错误或资源繁忙同样会影响实际时刻。

核对下一次触发和真实执行结果

systemctl --user list-timers --all linux-course-clock.timer
systemctl --user status linux-course-clock.timer
journalctl --user -u linux-course-clock.service --since "5 minutes ago" --no-pager
systemctl --user show linux-course-clock.service -p Result -p ExecMainStatus
timedatectl

先确认 list-timers 中有预期的下一次时间,再过一至两分钟查看用户日志中的时间输出和结束记录。oneshot 服务成功完成后也可能是 inactive,因此同时检查 Result 和 ExecMainStatus,不能只看 active 字样。日志不可见时还要检查实际执行用户与日志访问条件。

timedatectl 显示服务器时区和时钟同步情况,需与业务希望采用的时区核对。默认日历时间不要误当成另一个地区的当地时间;需要显式时区时,先按 systemd 时间表达式手册验证该表达式,而不是只改显示格式。

任务需要在注销后继续运行时,应核对用户管理器生命周期和本机 linger 策略。linger 可使用户管理器在启动时建立、注销后继续保留,但是否启用应遵循主机策略;本练习不要求直接改变该策略。仅 enable 定时器不足以证明它在每种注销或重启条件下都能运行。生产系统定时器还需设计专用账号、绝对路径、最小权限与资源限制。

管理重叠、失败并撤销实验

cron 命令超过调度间隔时,后一次可能与前一次重叠,可按环境考虑 flock 等锁。systemd 定时器到期时,若同一目标服务已处于活动状态,不会自动另建第二个并发实例;但其他单元或手动命令仍可能争用资源,所以应用层的并发控制依然必要。

按下面步骤撤销,仅删除这次新建的两个文件。停止 timer 不一定终止已启动的服务,先检查本次练习服务是否结束。Persistent 还可能留下触发时间戳;安装版本支持时,在停用 timer 之后、删除单元文件之前,可按手册用 systemctl --user clean --what=state linux-course-clock.timer 清理该单元的状态,并确认命令结果。不要把清理命令用于其他单元。

systemctl --user disable --now linux-course-clock.timer
rm -- "$HOME/.config/systemd/user/linux-course-clock.timer" "$HOME/.config/systemd/user/linux-course-clock.service"
systemctl --user daemon-reload
systemctl --user list-timers --all linux-course-clock.timer

再次检查列表确认本次 timer 已移除。若添加过 cron 示例,则用 crontab -e 只删除那一行,再用 crontab -l 核对;不要删除整份已有计划。实际自动化还应独立发现失败或未执行的任务,并准备恢复流程,不能仅依赖成功提示。

官方参考资料