你对 systemd 定时器的喜爱不够
« 你对 systemd 定时器的喜爱不够 2026年5月5日 2139 字 9 分钟阅读时间 我最喜欢的隐喻技术术语是 "cron job":“即使 cron 并不字面上是负责按计划执行操作的守护进程,我们仍然将这一术语应用于任何看起来像 cron、听起来像 cron 的东西。正如 Patrick McKenzie 喜欢指出的,cron job 是最有用的计算原语之一。对于几乎所有人都有的许多用例来说,它们的实用性几乎是显而易见的:每天做这个;每月做那个。然而。你可能不应该使用字面意义上的 cron(或它更现代的版本)来进行计划任务!在 2026 年,有更现代的选项可用,而我最喜欢的是谦虚的 systemd 定时器。我喜欢 systemd 定时器。如果你还没有爱上它们,也许我可以向你展示你为什么也应该爱上它们。 我对 cron 的看法?过时了吗? systemd 定时器是一种单元类型,在特定时间表上调度其他单元(通常是服务)。 (systemd 服务单元如何工作是另一篇文章,但你可以逻辑上将 systemd 定时器的 .service 目标视为脚本。)定时器有效地替代了传统的 cron 守护进程(尽管你可以假设同时运行两者),定时器日历设置提供了一些相似性,以帮助弥补传统 cron 风格表达式之间的差距。此时,systemd 的反对者们跃跃欲试,准备对定时器进行攻击,因为它们是 systemd 项目的一部分,并且取代了成熟(尽管笨拙)的技术。我宁愿不花时间争论 cron,所以简要考虑一下为什么像 systemd 定时器这样的新解决方案,从多年的经验中获益,比传统方案更好:模糊的 $PATH 设置使得 cron 脚本的执行难以预测。stdout 和 stderr 输出常常进入黑洞(并且通常被发送到主机的邮件系统,这通常不是你想要的结果。执行历史难以跟踪和查询。你可能会觉得熟悉调度语法很酷,但 01,31 04,05 1-15 1,6 * 对于人类来说并不容易或直观阅读。顺便提一下,定时器解决了所有这些问题(等等)。 定时器介绍的好时机 我们可以在没有很多形式的情况下讨论基础知识。首先你需要一个定时器执行的目标。在有 systemd 操作的 Linux 主机上,将以下单元内容放在 /etc/systemd/system/roulette.service 中,安装一个服务,以有 1/10 的概率关闭计算机: Systemd 字体用于突出显示字符串。字体用于突出显示关键字。字体用于突出显示类型和类名称。 [Unit] Description = 1/10的机会打破你的束缚 [Service] ExecStart = /usr/bin/env bash -c '[[ $(($RANDOM % 10)) == 0 ]] && systemctl poweroff || echo LIVE ANOTHER DAY' 更新:[2026-05-05 周二] Twitter 用户 HSVSphere 指出,服务选项 ExecCondition = 提供了一种本机方式来处理条件执行。这是一种更紧密集成的方式来表达“我是否继续执行?”我同意这在单元级别提供了一种更清晰的意图表达(我在这里为 NixOS 系统使用绝对路径): systemd 字体用于突出显示字符串。字体用于突出显示关键字。字体用于突出显示类型和类名称。 [Unit] Description = 1/10的机会打破你的束缚 [Service] ExecCondition = /run/current-system/sw/bin/bash -c '[[ $(($RANDOM % 10)) == 0 ]]' ExecStart = /run/current-system/sw/bin/systemctl poweroff 这与之前的 bash 条件具有相同的效果,你的日志中最终会有不同的措辞,在我看来,当条件满足时更清晰地表达了情况:2026 年 5 月 5 日 11:05:32 diesel systemd[3117]: 条件检查导致 1/10 的机会打破你的束缚被跳过。一般来说,充分利用 systemd 提供的选项比你自己编写脚本要好得多。(另一个例子是使用 OnFailure = 在你的服务脚本失败时反应,或使用 Restart = 在暂时性故障的情况下尝试恢复。)通过在 /etc/systemd/system/roulette.timer 中放置一个具有相同文件根的文件(roulette),将该服务与定时器关联: systemd 字体用于突出显示关键字。字体用于突出显示类型和类名称。 [Unit] Description = 迫在眉睫的毁灭 [Timer] OnCalendar = 10:00 [Install] WantedBy = timers.target 我所说的关联是,默认情况下,定时器的 Unit = 设置将选择一个名称相同且后缀为 .service 的服务单元。在这种情况下,roulette.service。如果你想要执行一个不同名称的服务,你总是可以更改这一点。我想立即指出几件事情:根据常规服务单元语义,ExecStart = 目标默认情况下不会作为 shell 命令运行。你应该将绝对路径目标视为一个脚本,或者在我们的例子中,作为一个期望字符串参数的解释器。例如,ExecStart = /usr/bin/echo Hello | /usr/bin/awk s
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡