用 systemd.timer 替代 crontab 更统一可控,依赖管理、日志集成、失败重试更原生;其核心是 timer 单元触发同名 service 单元,oncalendar 支持语义化时间表达与秒级精度,service 需显式声明环境、路径及重启策略。

用 systemd.timer 替代 crontab 不仅更统一、更可控,还能享受依赖管理、日志集成、失败重试等原生优势。关键不是简单“照搬 cron 表达式”,而是理解 systemd 的事件驱动模型和单元依赖逻辑。
理解 timer 单元的核心机制
systemd.timer 不是“每隔 X 时间执行一次命令”,而是“在某个时间点触发一个 .service 单元”。它由两部分组成:timer 单元(定义何时触发)和对应同名的 service 单元(定义执行什么)。二者通过命名绑定(如 backup.timer → backup.service),无需手动配置依赖。
- timer 单元本身不执行任务,只负责调度;真正干活的是 service 单元
- 启动 timer 单元(
systemctl start backup.timer)会自动启用关联 service,但 service 只在 timer 触发时才运行 - 所有执行记录、标准输出/错误都归入 journal,用
journalctl -u backup.service查看
编写 timer 单元:OnCalendar 是最常用也最灵活的方式
相比 cron 的五段式表达式,OnCalendar= 更语义化且支持更多精度(秒级)、多触发点和自然语言缩写,例如:
-
OnCalendar=*-*-* 02:00:00→ 每天凌晨 2 点整(注意:秒必须显式指定,否则默认为 :00) -
OnCalendar=Mon,Wed,Fri *-*-15 14:30:00→ 每月 15 日且是周一、三、五的下午 2:30 -
OnCalendar=hourly或OnCalendar=daily→ 内置别名,等价于*-*-* *:*:00和*-*-* 00:00:00 -
OnCalendar=on-boot + 10min→ 系统启动后 10 分钟执行一次(适合初始化类任务)
建议始终加上 AccuracySec=1s(默认 60s),避免因系统负载导致明显延迟;若任务对时间敏感,再配合 RandomizedDelaySec= 避免集群雪崩。
service 单元要适配 systemd 的生命周期管理
service 文件不能假设环境变量或当前工作目录与登录 shell 一致。需显式声明关键项:
-
Type=oneshot:适用于脚本类任务(默认类型,执行完即退出) -
ExecStart=/usr/local/bin/backup.sh:路径必须绝对;推荐用完整路径调用解释器,如/bin/bash /usr/local/bin/backup.sh -
WorkingDirectory=/var/lib/backup:明确工作目录,避免相对路径出错 -
Environment="PATH=/usr/local/bin:/usr/bin:/bin":显式设置 PATH,避免找不到命令 -
Restart=on-failure和RestartSec=30:可选,让失败任务自动重试一次(cron 默认不重试) -
WantedBy=multi-user.target:确保开机自启(timer 启用后,该行让 timer 自动随系统启动)
部署与调试:避免常见陷阱
写完两个单元文件后,放至 /etc/systemd/system/(用户级用 ~/.config/systemd/user/),然后:
- 重载配置:
sudo systemctl daemon-reload - 启用并启动 timer:
sudo systemctl enable --now backup.timer - 检查状态:
systemctl list-timers --all看是否列出了你的 timer 及下次触发时间 - 验证 service 是否正常:
sudo systemctl start backup.service手动触发一次,再查 journal - 注意:修改 timer 后,
systemctl restart backup.timer不会立即重新计算下次触发时间,需sudo systemctl daemon-reload && sudo systemctl restart backup.timer
不复杂但容易忽略:timer 单元默认不继承 login shell 的环境(如 ~/.bashrc 中的变量),所有依赖必须在 service 中显式声明或封装进脚本内。











