cron

出自 ArchWiki
(重定向自 Crontab

出自 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
  • 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

本文或章节已过时。

原因: 它还能工作吗?(在 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

原版 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 同时提供 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 手册页

另请参阅