Cron表达式转systemd定时器工具

免费工具,将cron表达式(分 时 日 月 星期)转换为systemd定时器的OnCalendar=语法。同时对日期与星期同时限定时的OR语义差异进行提示,适用于将cron任务迁移到Docker/Kubernetes环境。

cron字段与systemd组成部分对照表

cron表达式的5个字段与systemd的OnCalendar=语法对应关系如下。

位置 cron字段 systemd对应部分
1 分钟 时间部分的分钟(HH:MM:SS中的MM)
2 小时 时间部分的小时(HH:MM:SS中的HH)
3 日(日期) 日期部分的日(年-月-日中的日)
4 月 日期部分的月(年-月-日中的月)
5 星期 星期前缀(例如Mon、Mon..Fri)

由于cron没有秒字段,systemd的秒部分始终为"00"。当日期和月份均不受限(*)时,systemd的年-月-日部分可以完全省略。

cron星期编号与systemd星期缩写对照表

cron用0〜7的数字表示星期,而systemd使用英文星期缩写。

cron星期编号 systemd星期缩写
0 (7) Sun
1 Mon
2 Tue
3 Wed
4 Thu
5 Fri
6 Sat

常见转换示例

cron表达式 OnCalendar= 含义
* * * * * OnCalendar=*-*-* *:*:00 每分钟执行一次(相当于cron的"* * * * *")。
*/15 * * * * OnCalendar=*:00/15:00 每15分钟执行一次。
0 9 * * 1-5 OnCalendar=Mon..Fri 09:00:00 工作日(周一至周五)上午9点执行。
0 0 1 * * OnCalendar=*-*-01 00:00:00 每月1日0点执行。
0 0 1 1 * OnCalendar=*-01-01 00:00:00 每年1月1日0点执行。
0 0 1 * 1 OnCalendar=Mon *-*-01 00:00:00 同时指定日期(1日)与星期(周一)的示例。在cron中这是OR条件(满足其一即可执行),但在systemd中会变为AND条件(必须同时满足才执行),因此转换结果的执行频率会低于原cron表达式。

同时限定日期与星期(均不为"*")的cron表达式,无法通过systemd的OnCalendar语法准确转换,因为cron在此情形下是OR语义,而systemd始终是AND语义。建议将此类情况拆分为两个独立的systemd定时器。

把 cron 式改写成 systemd 定时器

要把排程由 `cron` 迁往 `systemd` 定时器,便须把「分 时 日 月 星期」这五个字段译成 `OnCalendar=` 的写法。输入一条 cron 式,本工具便产出与之对应的 `OnCalendar=`,并把值得当心之处一并报作警告。

**这道转换中最要紧的,是「日」与「星期」同时给出时二者的脾性。** cron 把这两者读作**或**:`0 0 1 * 1` 意为「每月一日**或**每个星期一」,一个月里可能跑上五回。**而 systemd 的 `OnCalendar=` 却把同样的写法当作与。** 它成了「一日**且**星期一」,一年里只跑一两回。**机械地照搬,作业的跑动频次便悄然变了。** 本工具会侦出这一组合并向您示警。

转换的步骤

  1. 输入 cron 式 把分、时、日、月、星期这五个字段以空格分开写出。
  2. 查看生成的 OnCalendar= 您会得到与之对应的 systemd 写法。
  3. 阅读警告 **日与星期同时给出时,含义的变动会被点出来。**
  4. 誊进定时器单元 贴进 `.timer` 文件的 `[Timer]` 一节即可。

用好本工具的小技巧

  • 如果将日期与星期都设为非"*",cron的OR条件无法用systemd的AND条件准确表达。请查看转换结果中的警告,必要时拆分为两个定时器。
  • 一些最小化的容器基础镜像(例如基于Alpine的Docker镜像)根本不包含cron守护进程,此时systemd定时器是迁移计划任务的实用替代方案。
  • 可以使用"systemd-analyze calendar"命令验证生成的OnCalendar行,该命令还会显示接下来的计划执行时间。
  • 与单行crontab不同,systemd定时器由两个文件组成:.timer单元(计划部分)和.service单元(实际执行的任务)。
  • 对于不同时组合日期与星期的简单计划(每小时、每天、每周等),本转换结果始终是精确的。

这些场景会用到

迁往容器环境

**容器里往往没有 cron 守护进程,排程便须迁往 systemd 定时器或别的安排。**

想留下运行的记录

systemd 定时器会在 journald 里留痕,事后可以追查。

想让作业之间有依赖

**「在另一单元启动之后再跑」这类事,cron 表达不出来。**

盘点既有的排程

把一批 cron 式一并转换,正是核对间隔是否合乎本意的好时机。

排程的术语

cron 式
以分、时、日、月、星期五个字段表示时刻的写法。**日与星期被读作或。**
OnCalendar=
在 systemd 定时器中言明时刻的键。**日与星期被读作与。**
定时器单元
扩展名为 `.timer` 的文件,它启动同名的 `.service`。
Persistent=
**该跑的时刻系统若正停着,此设定会让定时器开机后补跑。** 其行事与 cron 的 `anacron` 相仿。
AccuracySec=
触发时刻的容差。默认有一分钟的宽裕,故须严丝合缝时应予指明。
journald
systemd 的日志收集机制。由定时器启动的作业,其输出亦存于此。

常见问题

OnCalendar=的格式为"星期 年-月-日 时:分:秒"(星期和日期部分可省略)。"*"表示"任意值",".."表示范围,"/"表示间隔,","用于列举多个值。例如"Mon..Fri 09:00:00"表示工作日上午9点整。

systemd定时器提供了cron本身没有的功能:通过journalctl集中管理执行日志、等待依赖的服务先启动、以及可配置的失败自动重试。容器基础镜像也越来越倾向于不包含cron守护进程以保持轻量,因此在基于systemd的主机上,定时器成为自然的替代方案。

按照Vixie cron的语义,当日期与星期都被限定(非"*")时,只要满足其中一个条件任务就会执行,即OR条件。而systemd的OnCalendar语法始终将星期和日期部分作为AND条件处理(必须同时满足)。本工具会检测到这种情况并显示警告,提示转换结果的执行频率可能低于原cron表达式。

对于不同时组合日期与星期的简单计划,转换结果是精确的。但如果出现了OR语义警告,实际执行频率可能与预期不同,建议在部署到生产环境前使用"systemd-analyze calendar"命令确认接下来的计划执行时间。

标准的5字段cron表达式没有秒字段,因此生成的OnCalendar行中的秒部分始终为"00"。systemd的OnCalendar语法本身支持指定秒,但本工具从cron转换时不会使用该功能。
工具君

闲话 ― 从cron到systemd定时器

systemd大约于2010年前后作为管理Linux启动流程的初始化系统出现,此后成为大多数发行版的标准配置。其中的定时器功能与cron类似,提供周期性执行能力,但增加了cron从未拥有的功能:依赖关系解析、通过journalctl集中管理执行日志,以及可配置的失败重试行为。

与此同时,Docker和Kubernetes等容器环境中,为了保持镜像体积小巧,越来越多的基础镜像根本不包含cron守护进程。当需要在此类环境中运行周期性任务时,现实的选择要么依赖Kubernetes CronJob等编排层面的机制,要么在主机本身运行systemd的情况下交给systemd定时器处理。

OnCalendar=语法比cron的5个字段表达能力更强,支持指定年份精度和秒级精度,但正因如此,一些cron表达式(同时限定日期与星期的OR条件)在简单转换下会改变含义。本工具明确地提示这一警告,正是为了让迁移过程不至于陷入这个容易被忽略的陷阱。