cron

来自 ArchWiki

来自 Wikipedia

cron 是类 Unix 计算机操作系统中基于时间的作业调度器。 cron 使用户能够调度作业(命令或 shell 脚本)在特定的时间、日期或间隔定期运行。它通常用于自动化系统维护或管理。

安装

有许多 cron 实现,但默认情况下未安装任何实现,因为基本系统使用 systemd/Timers 代替。请参阅 Gentoo wiki 的 cron 指南,其中提供了比较。

可用软件包

配置

激活和自动启动

安装后,守护进程默认不会启用。已安装的软件包可能提供一个服务,可以使用 systemctl 进行控制。例如,cronie 使用 cronie.service

检查 /etc/cron.daily/ 和类似目录以查看存在哪些任务。激活 cron 服务将触发所有这些任务。

注意: cronie 提供了 0anacron 每小时 任务,这允许延迟运行其他任务,例如,如果计算机在标准执行时刻被关闭。

处理任务错误

cron 注册来自 stdoutstderr 的输出,并尝试通过 sendmail 命令将其作为电子邮件发送到用户的邮件池。如果未找到 /usr/bin/sendmail,Cronie 将禁用邮件输出。为了将邮件写入用户的邮件池,系统上必须运行 smtp 守护进程,例如 opensmtpd。否则,您可以安装一个提供 sendmail 命令的软件包,并将其配置为将邮件发送到远程邮件交换器。您还可以使用 -m 选项并编写自定义脚本来记录消息。

提示: 可以使用 Postfix#本地邮件 将输出发送到本地系统用户。
  1. 编辑 cronie.service 单元文件。
  2. 安装 esmtpAURmsmtpopensmtpdsSMTP,或编写自定义脚本。

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 示例

安装 esmtpAURprocmailAUR

安装后配置路由

/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

发生的情况是这样的

  1. cron 运行脚本
  2. 一旦 cron 看到一些输出,它就会运行您的 MTA,并为其提供标头。它保持管道打开,因为作业尚未完成,并且可能还有更多输出。
  3. MTA 打开与 postfix 的连接,并在等待正文的其余部分时保持该连接打开。
  4. 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
  • 分钟 值可以是 0 到 59。
  • 小时 值可以是 0 到 23。
  • 值可以是 1 到 31。
  • 月份 值可以是 1 到 12。
  • 星期 值可以是 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

此文章或章节已过时。

原因:它还能工作吗?(在 Talk:Cron#cron x.org aplications 中讨论)

或者可以手动定义它们(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

Vanilla 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 时,请注意后台处理目录是 /var/spool/fcron,并且使用 fcrontab 命令而不是 crontab 来编辑用户 crontab。这些 crontab 以二进制格式存储,文本版本与它们相邻,在后台处理目录中为 foo.orig。由于行为上的这种差异,任何手动编辑用户 crontab 的脚本可能都需要调整。

一个可以帮助将传统用户 crontab 转换为 fcron 格式的快速 scriptlet

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 同时提供 cronanacron 功能: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 可以显示您是否有任何此类问题。
提示: 要阻止发送输出并停止电子邮件警报,请在每个 cron 作业的行尾添加 >/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 条目可以具有的格式之一,即空格分隔的字段,指定

  1. @period
  2. ID=jobname(此标签特定于 dcron)
  3. command

crontab 条目的另一个标准格式是

  1. 分钟
  2. 小时
  3. 星期几
  4. command

crontab 文件本身通常存储为 /var/spool/cron/username。例如,root 的 crontab 位于 /var/spool/cron/root

有关更多信息和配置示例,请参阅 crontab 手册页

另请参阅