cron
出自 Wikipedia
- cron 是类 Unix 计算机操作系统中基于时间的作业调度程序。 cron 允许用户调度作业(命令或 shell 脚本)在特定时间、日期或间隔定期运行。它通常用于自动化系统维护或管理。
安装
有许多 cron 实现,但默认情况下没有安装任何一个,因为基本系统使用 systemd/Timers 代替。请参阅 Gentoo wiki 的 cron 指南,其中提供了比较。
可用软件包
- cronie
- fcron
- dcronAUR
- vixie-cronAUR
- scron-gitAUR
配置
激活与自启动
安装后,守护程序默认不会启用。已安装的软件包可能提供了一个服务,可以通过 systemctl 控制。例如,cronie 使用 cronie.service
。
检查 /etc/cron.daily/
和类似的目录以查看存在哪些作业。激活 cron 服务将触发所有这些作业。
0anacron
每小时 作业,允许延迟运行其他作业,例如,如果计算机在标准执行时刻被关闭。处理任务错误
cron 注册来自 stdout 和 stderr 的输出,并尝试通过 sendmail
命令将其作为电子邮件发送到用户的假脱机目录。如果找不到 /usr/bin/sendmail
,Cronie 将禁用邮件输出。为了将邮件写入用户的假脱机目录,系统上必须运行 smtp 守护程序,例如 opensmtpd。否则,您可以安装一个提供 sendmail 命令的软件包,并将其配置为将邮件发送到远程邮件交换器。您还可以使用 -m
选项并编写自定义脚本来记录消息。
使用 sSMTP 的示例
sSMTP 是一个仅发送的 sendmail 模拟器,它将来自本地计算机的电子邮件传递到 smtp 服务器。虽然目前没有活跃的维护者,但它仍然是将邮件传输到已配置邮件中心的最简单方法。没有要运行的守护程序,并且配置可以像编辑单个配置文件中的 3 行一样简单(如果您的主机被信任通过您的邮件中心中继未经身份验证的电子邮件)。sSMTP 不接收邮件、展开别名或管理队列。
安装 ssmtpAUR,它创建从 /usr/bin/sendmail
到 /usr/bin/ssmtp
的符号链接。然后您必须编辑 /etc/ssmtp/ssmtp.conf
。有关详细信息,请参阅 sSMTP。创建到 /usr/bin/sendmail
的符号链接可确保像 S-nail 这样的程序(或任何提供 /usr/bin/mail
的软件包)可以正常工作,而无需修改。
重启 cronie.service
以确保它检测到您现在已安装 /usr/bin/sendmail
。
使用 msmtp 的示例
安装 msmtp-mta,它创建从 /usr/bin/sendmail
到 /usr/bin/msmtp
的符号链接。重启 cronie.service
以确保它检测到新的 sendmail
命令。然后您必须提供一种方法让 msmtp
将您的用户名转换为电子邮件地址。
然后将 MAILTO
行添加到您的 crontab,如下所示
MAILTO=your@email.com
或者 创建 /etc/msmtprc
并附加此行
aliases /etc/aliases
并创建 /etc/aliases
your_username: your@email.com # Optional: default: your@email.com
然后 修改 cronie 守护程序的配置,将 ExecStart
命令替换为
ExecStart=/usr/bin/crond -n -m '/usr/bin/msmtp -t'
使用 esmtp 的示例
安装后配置路由
/etc/esmtprc
identity myself@myisp.com hostname mail.myisp.com:25 username "myself" password "secret" starttls enabled default mda "/usr/bin/procmail -d %T"
Procmail 需要 root 权限才能在传递模式下工作,但如果您以 root 身份运行 cron 作业,则这不是问题。
为了测试一切工作正常,创建一个包含 "test message"
的文件 message.txt
。
从同一目录运行
$ sendmail user_name < message.txt
然后
$ cat /var/spool/mail/user_name
您现在应该看到测试消息以及发送时间和日期。
所有作业的错误输出现在将重定向到 /var/spool/mail/user_name
。
由于权限问题,很难创建电子邮件并将其发送给 root 用户(例如 su -c ""
)。您可以要求 esmtp
将所有 root 用户的电子邮件转发给普通用户,方法是
/etc/esmtprc
force_mda="user-name"
~/.esmtprc
中创建具有相同内容的本地配置。运行以下命令以确保它具有正确的权限
$ chmod 710 ~/.esmtprc然后使用
message.txt
完全按照之前的步骤重复测试。使用 opensmtpd 的示例
安装 opensmtpd。
编辑 /etc/smtpd/smtpd.conf
。以下配置允许本地传递
listen on localhost action "local" mbox alias <aliases> match for local action "local"
您可以继续测试它。首先启动 smtpd.service
。然后执行
$ echo test | sendmail user
用户 可以使用任何能够处理 mbox 格式的 阅读器 检查他们的邮件,或者只查看文件 /var/spool/mail/user
。如果一切按预期进行,您可以启用 opensmtpd 以便将来启动。
这种方法的优点是不将本地 cron 通知发送到远程服务器。缺点是您需要运行新的守护程序。
- 在编写本文时,Arch opensmtpd 软件包未在
/var/spool/smtpd
下创建所有需要的目录,但守护程序会发出警告,说明所需的所有权和权限。只需按照建议创建它们即可。 - 即使建议的配置不接受远程连接,但使用 iptables 或类似的工具阻止端口 25 也是一个健康的预防措施,可以增加额外的安全层。
长时间运行的 cron 任务
假设此程序由 cron 调用
#!/bin/sh echo "I had a recoverable error!" sleep 1h
会发生这种情况
- cron 运行脚本
- 一旦 cron 看到一些输出,它就会运行您的 MTA,并为其提供标头。它保持管道打开,因为作业尚未完成,并且可能还有更多输出。
- MTA 打开与 postfix 的连接,并在等待其余正文时保持该连接打开。
- postfix 在不到一小时后关闭空闲连接,您会收到类似这样的错误
smtpmsg='421 … Error: timeout exceeded' errormsg='the server did not accept the mail'
要解决此问题,您可以使用来自 moreutils 的命令 chronic 或 sponge。摘自它们各自的 man 手册
- chronic(1)
- chronic 运行一个命令,并安排仅在该命令失败(以非零值退出或崩溃)时才显示其标准输出和标准错误。如果命令成功,则任何无关的输出都将被隐藏。
- sponge(1)
- sponge 读取标准输入并将其写入指定的文件。与 shell 重定向不同,sponge 在打开输出文件之前会吸收其所有输入……如果未指定输出文件,则 sponge 输出到 stdout。
Chronic 也会缓冲命令输出,然后再打开其标准输出。
Crontab 格式
crontab 的基本格式是
minute hour day_of_month month day_of_week command
- minute 值可以从 0 到 59。
- hour 值可以从 0 到 23。
- day_of_month 值可以从 1 到 31。
- month 值可以从 1 到 12。
- day_of_week 值可以从 0 到 6,其中 0 表示星期日。
空格用于分隔字段。要微调您的计划,您还可以使用以下符号之一
符号 | 描述 |
---|---|
* | 通配符,指定每个可能的时间间隔 |
, | 列表,用逗号分隔多个值。 |
- | 指定两个数字之间的范围,用连字符分隔 |
/ | 使用斜杠指定周期/频率 |
例如,行
*/5 9-16 * 1-5,9-12 1-5 ~/bin/i_love_cron.sh
将在工作日的上午 9 点到下午 4:55 以五分钟的间隔执行脚本 i_love_cron.sh
,但六月、七月和八月除外。
此外,crontab 还有一些特殊关键字
关键字 | 描述 |
---|---|
@reboot | 启动时 |
@yearly | 每年一次 |
@annually | 与 @yearly 相同 |
@monthly | 每月一次 |
@weekly | 每周一次 |
@daily | 每天一次 |
@midnight | 与 @daily 相同 |
@hourly | 每小时一次 |
例如
@reboot ~/bin/i_love_cron.sh
将在启动时执行脚本 i_love_cron.sh
。
更多信息请参见: https://www.adminschoice.com/crontab-quick-reference
更多示例和高级配置技术可以在下面找到。
基本命令
Crontab 永远不应直接编辑;相反,您应该使用 crontab
程序来处理您的 crontab。
查看您的 crontab
$ crontab -l
编辑您的 crontab
$ crontab -e
删除所有您的 crontab
$ crontab -r
如果您有保存的 crontab 并且想要完全覆盖旧的 crontab
$ crontab saved_crontab_filename
从命令行覆盖 crontab (Wikipedia:stdin)
$ crontab -
编辑其他用户的 crontab
# crontab -u username -e
相同的格式(在命令后附加 -u username
)也适用于列出和删除 crontab。
示例
条目
01 * * * * /bin/echo Hello, world!
在每个月每一天的每小时的第一分钟(即 12:01、1:01、2:01 等)运行命令 /bin/echo Hello, world!
。
类似地
*/5 * * jan mon-fri /bin/echo Hello, world!
在一月份的工作日每五分钟运行相同的作业(即 12:00、12:05、12:10 等)。
该行(如 crontab(5) 中所述)
*0,*5 9-16 * 1-5,9-12 1-5 /home/user/bin/i_love_cron.sh
将在夏季(六月、七月和八月)以外的每个月的工作日(周一至周五)的上午 9 点到下午 5 点(不包括下午 5 点本身)以五分钟的间隔执行脚本 i_love_cron.sh
。
周期性设置也可以像此 crontab 模板中一样输入
# Chronological table of program loadings # Edit with "crontab" for proper functionality, "man 5 crontab" for formatting # User: johndoe # mm hh DD MM W /path/progam [--option]... ( W = weekday: 0-6 [Sun=0] ) 21 01 * * * /usr/bin/systemctl hibernate @weekly $HOME/.local/bin/trash-empty
以下是一些不言自明的 crontab 语法示例
30 4 echo "It is now 4:30 am." 0 22 echo "It is now 10 pm." 30 15 25 12 echo "It is 3:30pm on Christmas Day." 30 3 * * * echo "Remind me that it's 3:30am every day." 0 * * * * echo "It is the start of a new hour." 0 6 1,15 * * echo "At 6am on the 1st and 15th of every month." 0 6 * * 2,3,5 echo "At 6am on Tuesday, Wednesday and Thursdays." 59 23 * * 1-5 echo "Just before midnight on weekdays." 0 */2 * * * echo "Every two hours." 0 20 * * 4 echo "8pm on a Thursday." 0 20 * * Thu echo "8pm on a Thursday." */15 9-17 * * 2-5 echo "Every 15 minutes from 9am-5pm on weekdays." @yearly echo "Happy New Year!"
默认编辑器
要使用备用默认编辑器,请在 shell 初始化脚本中定义 EDITOR
环境变量,如 环境变量 中所述。
作为普通用户,su
需要代替 sudo
使用,以便正确拉取环境变量
$ su -c "crontab -e"
为了拥有此别名,需要 printf
来携带任意字符串,因为 su
在新 shell 中启动
alias scron="su -c $(printf "%q " "crontab -e")"
运行基于 X.org 服务器的应用程序
Cron 不在 X.org 服务器下运行,因此它无法知道启动 X.org 服务器应用程序所需的环境变量,因此必须定义它们。可以使用像 xuserrun-gitAUR 这样的程序来执行此操作
17 02 * ... /usr/bin/xuserrun /usr/bin/xclock
或者可以手动定义它们(echo $DISPLAY
将给出当前的 DISPLAY 值)
17 02 * ... env DISPLAY=:0 /usr/bin/xclock
如果在 cron 中运行 notify-send 以进行桌面通知,notify-send 会将值发送到 dbus。因此它需要告诉 dbus 连接到正确的总线。地址可以通过检查 DBUS_SESSION_BUS_ADDRESS 环境变量并将其设置为相同的值来找到。因此
17 02 * ... env DBUS_SESSION_BUS_ADDRESS=your-address notify-send 'Foo bar'
如果通过例如 SSH 完成,则需要授予权限
# xhost +si:localuser:$(whoami)
异步作业处理
如果您经常关闭计算机,但又不想错过作业,则有一些解决方案可用(从最容易到最难)
Cronie
cronie 自带 anacron。项目主页上说
Cronie 包含标准的 UNIX 守护进程 crond,它在预定的时间运行指定的程序以及相关工具。它基于原始的 cron,并具有安全性和配置增强功能,例如使用 pam 和 SELinux 的能力。
Dcron
原版 dcronAUR 支持异步作业处理。只需将其与 @hourly、@daily、@weekly 或 @monthly 以及作业名称一起使用,就像这样
@hourly ID=greatest_ever_job echo This job is very useful.
Cronwhip
cronwhipAUR 是一个自动运行错过 cron 作业的脚本;它与以前默认的 cron 实现 dcron 一起工作。另请参阅论坛帖子。
Anacron
Anacron 是 dcron 的完全替代品,它可以异步处理作业。
它由 cronie 提供。配置文件是 /etc/anacrontab
。有关格式的信息可以在 anacrontab(5) 中找到。运行 anacron -T
将测试 /etc/anacrontab
的有效性。
Fcron
与 anacron 类似,fcron 假设计算机并非始终运行,并且与 anacron 不同,它可以安排短于一天的间隔的事件,这对于经常挂起/休眠的系统(例如笔记本电脑)可能很有用。与 cronwhip 类似,fcron 可以运行在计算机停机期间本应运行的作业。
当用 fcron 替换 cronie 时,请注意 spool 目录是 /var/spool/fcron
,并且使用 fcrontab
命令而不是 crontab 来编辑用户 crontab。这些 crontab 以二进制格式存储,文本版本与它们相邻,在 spool 目录中为 foo.orig。任何手动编辑用户 crontab 的脚本都可能需要根据此行为差异进行调整。
一个可能有助于将传统用户 crontab 转换为 fcron 格式的快速脚本片段
cd /var/spool/cron && ( for ctab in *; do fcrontab ${ctab} -u ${ctab} done )
另请参阅论坛帖子。
确保互斥性
如果您运行可能长时间运行的作业(例如,由于许多更改或特定的慢速网络连接,备份可能会突然运行很长时间),那么 flock
(util-linux) 可以确保 cron 作业不会第二次启动。
5,35 * * * * /usr/bin/flock -n /tmp/lock.backup /root/make-backup.sh
Cronie
cronie 的相关文件层次结构如下
/etc/ |----- cron.d/ | ----- 0hourly |----- cron.minutely/ |----- cron.hourly/ | ----- 0anacron |----- anacrontab |----- cron.daily/ |----- cron.monthly/ |----- cron.weekly/ |----- crontab |----- cron.deny
Cronie 同时提供 cron 和 anacron 功能:cron 以规则的时间间隔(精确到一分钟)运行作业,只要系统在指定时间可用,而 anacron 以天为单位指定的间隔执行命令。与 cron 不同,它不假设系统持续运行。每当系统启动时,anacron 都会检查是否有任何本应运行的作业,并相应地处理它们。
cron 作业可以在 /etc/cron.d
目录中的类似 crontab 的文件中定义,或者添加到 /etc/crontab
文件中。请注意,后者默认不存在,但如果存在则会使用。按照 /etc/cron.d/0hourly
的指示,/etc/cron.hourly
中的任何可执行文件都将每小时运行一次(默认情况下在每小时的第 1 分钟)。如果 /etc/cron.d/0minutely
中有相应的指示,则每分钟执行 /etc/cron.minutely
中的文件。可执行文件通常是 shell 脚本,也可以使用指向可执行文件的符号链接。/etc/cron.deny
文件包含不允许使用 crontab 的用户列表,如果没有此文件,则只有 /etc/cron.allow
中列出的用户才能使用它。
Anacron 的工作方式类似,通过执行 /etc/cron.daily
、/etc/cron.weekly
和 /etc/cron.monthly
目录中的文件,这些文件根据所需的作业频率放置在那里。cron 作业 /etc/cron.hourly/0anacron
确保每天运行一次 anacron 以执行其待处理的任务。
- Cronie 使用
run-parts
来执行不同目录中的脚本。文件名不应包含任何点 (.),因为run-parts
在其默认模式下会静默忽略它们(参见 run-parts(8))。名称必须仅包含大小写字母、数字、下划线和减号。 systemctl status cronie
的输出可能会显示诸如CAN'T OPEN (/etc/crontab): No such file or directory
之类的消息。但是,这可以忽略,因为 cronie 不需要一个。- Cronie 对
/etc/cron.d/0hourly
的权限特别敏感。如果/etc/cron.d/0hourly
损坏或权限不正确,则/etc/cron.d/{hourly,weekly,daily} ... etc
中的任何任务(包括 anacron 启动器)都不会运行。pacman -Qkk cronie
可以显示您是否有任何此类问题。
>/dev/null 2>&1
以将输出重定向到 /dev/null。0 1 5 10 * /path/to/script.sh >/dev/null 2>&1您还可以在 crontab 文件中设置
MAILTO=””
变量以禁用电子邮件警报。Dcron
cron 守护进程解析一个名为 crontab
的配置文件。系统上的每个用户都可以维护单独的 crontab 文件以单独安排命令。root 用户的 crontab 用于安排系统范围的任务(尽管用户可以选择使用 /etc/crontab
或 /etc/cron.d
目录,具体取决于他们选择的 cron 实现)。
/var/spool/cron/root
# Run command at a scheduled time # Edit this 'crontab -e' for error checking, man 1 crontab for acceptable format # <@freq> <tags and command> @hourly ID=sys-hourly /usr/sbin/run-cron /etc/cron.hourly @daily ID=sys-daily /usr/sbin/run-cron /etc/cron.daily @weekly ID=sys-weekly /usr/sbin/run-cron /etc/cron.weekly @monthly ID=sys-monthly /usr/sbin/run-cron /etc/cron.monthly # mm hh DD MM W /path/command (or tags) # W = week: 0-6, Sun=0 21 01 * * * /usr/bin/systemctl suspend
这些行举例说明了 crontab 条目可以具有的格式之一,即空格分隔的字段,指定
- @period
- ID=jobname(此标签特定于 dcron)
- command
crontab 条目的另一种标准格式是
- 分钟
- 小时
- 日
- 月
- 星期几
- command
crontab 文件本身通常存储为 /var/spool/cron/username
。例如,root 的 crontab 位于 /var/spool/cron/root
有关更多信息和配置示例,请参阅 crontab 手册页。
另请参阅
- Gentoo Linux Cron 指南
- crontab.guru - cronjob 表达式在线编辑器
- cron-notifyAUR 是一个 FreeDesktop.org 兼容的通知服务,用于定期请求在执行命令之前确认。命令在自定义配置文件中配置。