我如何在 Linux 中使用 cron

没有时间执行命令?使用 cron 调度任务意味着程序可以运行,但您不必熬夜。
806 位读者喜欢这篇文章。
Top 5 Linux pain points in 2017

互联网档案馆书籍图片。由 Opensource.com 修改。CC BY-SA 4.0

作为系统管理员的挑战之一(在众多优势中)是在您宁愿睡觉时运行任务。例如,某些任务(包括定期重复发生的任务)需要在夜间或周末运行,那时预计没有人会使用计算机资源。我没有时间在晚上运行必须在非工作时间运行的命令和脚本。而且我不想在凌晨起床启动备份或重大更新。

相反,我使用两个服务实用程序,它们允许我在预定时间运行命令、程序和任务。cronat 服务使系统管理员能够安排任务在未来的特定时间运行。at 服务指定在特定时间运行的一次性任务。cron 服务可以安排重复性任务,例如每天、每周或每月。

在本文中,我将介绍 cron 服务以及如何使用它。

常见(和不常见)的 cron 用途

我使用 cron 服务来调度显而易见的事情,例如每天凌晨 2 点进行的定期备份。我也将其用于不太明显的事情。

  • 我的许多计算机上的系统时间(即操作系统时间)都是使用网络时间协议 (NTP) 设置的。虽然 NTP 设置了系统时间,但它不会设置可能漂移的硬件时间。我使用 cron 根据系统时间设置硬件时间。
  • 我还在每天清晨运行一个 Bash 程序,在每台计算机上创建一个新的“每日消息”(MOTD)。它包含诸如磁盘使用情况之类的信息,这些信息应该是最新的才能有用。
  • 许多系统进程和服务,如 LogwatchlogrotateRootkit Hunter,都使用 cron 服务来调度任务并每天运行程序。

crond 守护进程是启用 cron 功能的后台服务。

cron 服务检查 /var/spool/cron/etc/cron.d 目录以及 /etc/anacrontab 文件中的文件。这些文件的内容定义了要以各种间隔运行的 cron 作业。各个用户的 cron 文件位于 /var/spool/cron 中,系统服务和应用程序通常在 /etc/cron.d 目录中添加 cron 作业文件。/etc/anacrontab 是一种特殊情况,将在本文后面介绍。

使用 crontab

cron 实用程序基于 cron 表 (crontab) 中指定的命令运行。每个用户,包括 root 用户,都可以拥有一个 cron 文件。这些文件默认情况下不存在,但可以使用 crontab -e 命令在 /var/spool/cron 目录中创建,该命令也用于编辑 cron 文件(请参见下面的脚本)。我强烈建议您不要使用标准编辑器(例如 Vi、Vim、Emacs、Nano 或任何其他可用的编辑器)。使用 crontab 命令不仅允许您编辑命令,还会在您保存并退出编辑器时重新启动 crond 守护进程。crontab 命令使用 Vi 作为其底层编辑器,因为 Vi 始终存在(即使在最基本的安装中也是如此)。

新的 cron 文件是空的,因此必须从头开始添加命令。我在我自己的 cron 文件中添加了下面的作业定义示例,只是作为一个快速参考,以便我知道命令的各个部分意味着什么。随意复制它供您自己使用。

# crontab -e
SHELL=/bin/bash
MAILTO=root@example.com
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

# backup using the rsbu program to the internal 4TB HDD and then 4TB external
01 01 * * * /usr/local/bin/rsbu -vbd1 ; /usr/local/bin/rsbu -vbd2

# Set the hardware clock to keep it in sync with the more accurate system clock
03 05 * * * /sbin/hwclock --systohc

# Perform monthly updates on the first of the month
# 25 04 1 * * /usr/bin/dnf -y update

crontab 命令用于查看或编辑 cron 文件。

上面代码中的前三行设置了默认环境。环境必须设置为给定用户所需的任何环境,因为 cron 不提供任何类型的环境。SHELL 变量指定执行命令时要使用的 shell。此示例指定了 Bash shell。MAILTO 变量设置 cron 作业结果将发送到的电子邮件地址。这些电子邮件可以提供 cron 作业的状态(备份、更新等),并且包含如果您从命令行手动运行程序将看到的输出。第三行设置了环境的 PATH。即使此处设置了路径,我始终将完全限定路径添加到每个可执行文件的前面。

上面的示例中有几行注释行,详细说明了定义 cron 作业所需的语法。我将分解这些命令,然后添加更多命令来向您展示 crontab 文件的一些更高级的功能。

01 01 * * * /usr/local/bin/rsbu -vbd1 ; /usr/local/bin/rsbu -vbd2

我的 /etc/crontab 中的这一行运行一个脚本,该脚本为我的系统执行备份。

这一行运行我自己编写的 Bash shell 脚本 rsbu,该脚本备份我的所有系统。此作业在每天凌晨 1:01 (01 01) 启动。时间规范的第三、第四和第五位中的星号 (*) 就像文件通配符一样,用于其他时间划分;它们指定“每月的每一天”、“每个月”和“每周的每一天”。此行运行我的备份两次;一次备份到内部专用备份硬盘驱动器,另一次备份到我可以带到保险箱的外部 USB 驱动器。

以下行使用系统时钟作为准确时间的来源来设置计算机上的硬件时钟。此行设置为每天凌晨 5:03 (03 05) 运行。

03 05 * * * /sbin/hwclock --systohc

此行使用系统时间作为来源来设置硬件时钟。

我过去使用第三个也是最后一个 cron 作业(已注释掉)在每个月的第一天凌晨 04:25 执行 dnfyum 更新,但我将其注释掉了,因此不再运行。

# 25 04 1 * * /usr/bin/dnf -y update

此行过去用于执行每月更新,但我已将其注释掉。

其他调度技巧

现在让我们做一些比这些基本知识更有趣的事情。假设您想在每个星期四下午 3 点运行一个特定作业。

00 15 * * Thu /usr/local/bin/mycronjob.sh

此行在每个星期四下午 3 点运行 mycronjob.sh

或者,也许您需要在每个季度结束后运行季度报告。cron 服务没有“月末最后一天”的选项,因此您可以改为使用下个月的第一天,如下所示。(这假设报告所需的数据将在作业设置为运行时准备就绪。)

02 03 1 1,4,7,10 * /usr/local/bin/reports.sh

此 cron 作业在季度结束后下个月的第一天运行季度报告。

以下显示了一个作业,该作业在上午 9:01 到下午 5:01 之间的每个小时过一分钟运行时运行。

01 09-17 * * * /usr/local/bin/hourlyreminder.sh

有时您希望在正常工作时间内定期运行作业。

我遇到过需要每两小时、三小时或四小时运行一次作业的情况。这可以通过将小时数除以所需的间隔来实现,例如 */3 表示每三小时,或 6-18/3 表示在早上 6 点到下午 6 点之间每三小时运行一次。其他间隔也可以类似地划分;例如,分钟位置中的表达式 */15 表示“每 15 分钟运行一次作业”。

*/5 08-18/2 * * * /usr/local/bin/mycronjob.sh

此 cron 作业在上午 8 点到下午 5:58 之间的每个小时内每五分钟运行一次。

需要注意的一件事:除法表达式必须导致余数为零,作业才能运行。这就是为什么,在本示例中,作业设置为在从上午 8 点到下午 6 点的偶数小时内每五分钟运行一次(08:05、08:10、08:15 等),但在任何奇数小时内都不运行。例如,该作业在晚上 9 点到上午 9:59 之间根本不会运行。

我相信您可以根据这些示例提出许多其他可能性。

限制 cron 访问

具有 cron 访问权限的普通用户可能会犯错误,例如,可能会导致系统资源(如内存和 CPU 时间)被淹没。为了防止可能的滥用,系统管理员可以通过创建 /etc/cron.allow 文件来限制用户访问权限,该文件包含有权创建 cron 作业的所有用户的列表。root 用户无法被阻止使用 cron。

通过阻止非 root 用户创建自己的 cron 作业,root 用户可能需要将其 cron 作业添加到 root crontab。“但是等等!” 您说。“那不是以 root 身份运行这些作业吗?” 不一定。在本文的第一个示例中,注释中显示的用户名字段可用于指定作业运行时要具有的用户 ID。这可以防止指定的非 root 用户的作业以 root 身份运行。以下示例显示了一个作业定义,该定义以用户“student”身份运行作业

04 07 * * * student /usr/local/bin/mycronjob.sh

如果未指定用户,则作业将以拥有 crontab 文件的用户身份运行,在本例中为 root 用户。

cron.d

目录 /etc/cron.d 是某些应用程序(如 SpamAssassinsysstat)安装 cron 文件的地方。由于没有 spamassassin 或 sysstat 用户,这些程序需要一个位置来查找 cron 文件,因此它们被放置在 /etc/cron.d 中。

下面的 /etc/cron.d/sysstat 文件包含与系统活动报告 (SAR) 相关的 cron 作业。这些 cron 文件具有与用户 cron 文件相同的格式。

# Run system activity accounting tool every 10 minutes
*/10 * * * * root /usr/lib64/sa/sa1 1 1
# Generate a daily summary of process accounting at 23:53
53 23 * * * root /usr/lib64/sa/sa2 -A

sysstat 软件包安装 /etc/cron.d/sysstat cron 文件以运行 SAR 的程序。

sysstat cron 文件有两行执行任务。第一行每 10 分钟运行 sa1 程序以收集存储在 /var/log/sa 目录中特殊二进制文件中的数据。然后,每天晚上 23:53,sa2 程序运行以创建每日摘要。

调度技巧

我在 crontab 文件中设置的一些时间看起来相当随意——在某种程度上它们确实如此。尝试调度 cron 作业可能具有挑战性,尤其是在作业数量增加时。我通常在我的每台计算机上只调度少量任务,这比我在某些生产和实验室环境中工作时要简单得多。

我管理的一个系统大约有十几个 cron 作业每天晚上运行,另外还有三到四个在周末或每月的第一天运行。这是一个挑战,因为如果太多作业同时运行——尤其是备份和编译——系统会耗尽 RAM 并几乎填满交换文件,这会导致系统抖动,而性能下降,因此什么都做不了。我们添加了更多内存并改进了我们调度任务的方式。我们还删除了一项编写得很差且使用大量内存的任务。

crond 服务假定主机计算机始终运行。这意味着,如果在计划运行 cron 作业期间计算机关闭,则它们将不会运行,直到下次计划运行时。如果它们是关键的 cron 作业,这可能会导致问题。幸运的是,还有另一个选项可以定期运行作业:anacron

anacron

anacron 程序执行与 crond 相同的功能,但它添加了运行跳过作业的功能,例如计算机关闭或因其他原因无法在一个或多个周期内运行作业。这对于笔记本电脑和其他关闭或置于睡眠模式的计算机非常有用。

一旦计算机打开并启动,anacron 就会检查已配置的作业是否错过了上次计划的运行。如果错过了,这些作业将立即运行,但仅运行一次(无论错过了多少个周期)。例如,如果每周作业因系统在您度假时关闭而未运行三周,则它将在您打开计算机后不久运行,但仅运行一次,而不是三次。

anacron 程序提供了一些简单的选项来运行定期调度的任务。只需将您的脚本安装到 /etc/cron.[hourly|daily|weekly|monthly] 目录中,具体取决于它们需要运行的频率。

这是如何工作的?该顺序比乍一看要简单。

  1. crond 服务运行 /etc/cron.d/0hourly 中指定的 cron 作业。
# Run the hourly jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
01 * * * * root run-parts /etc/cron.hourly

/etc/cron.d/0hourly 的内容导致位于 /etc/cron.hourly 中的 shell 脚本运行。

  1. /etc/cron.d/0hourly 中指定的 cron 作业每小时运行一次 run-parts 程序。
  2. run-parts 程序运行 /etc/cron.hourly 目录中的所有脚本。
  3. /etc/cron.hourly 目录包含 0anacron 脚本,该脚本使用此处显示的 /etdc/anacrontab 配置文件运行 anacron 程序。
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22
                                                                
#period in days   delay in minutes   job-identifier   command
1       5       cron.daily              nice run-parts /etc/cron.daily
7       25      cron.weekly             nice run-parts /etc/cron.weekly
@monthly 45     cron.monthly            nice run-parts /etc/cron.monthly

/etc/anacrontab 文件的内容在适当的时间运行 cron.[daily|weekly|monthly] 目录中的可执行文件。

  1. anacron 程序每天运行一次位于 /etc/cron.daily 中的程序;它每周运行一次位于 /etc/cron.weekly 中的作业,每月运行一次 cron.monthly 中的作业。请注意每行中指定的延迟时间,这些时间有助于防止这些作业与自身和其他 cron 作业重叠。

我没有将完整的 Bash 程序放在 cron.X 目录中,而是将它们安装在 /usr/local/bin 目录中,这使我可以从命令行轻松运行它们。然后我在适当的 cron 目录(例如 /etc/cron.daily)中添加一个符号链接。

anacron 程序并非设计为在特定时间运行程序。相反,它的目的是按照指定时间的间隔运行程序,例如每天凌晨 3 点(请参阅上面脚本中的 START_HOURS_RANGE 行)、星期日(开始新的一周)以及每月的第 一天。如果错过了一个或多个周期,anacron 将尽快运行错过的任务一次。

快捷方式

上面显示的 /etc/anacrontab 文件为我们提供了一个线索,说明如何为一些特定和常见的时间使用快捷方式。这些单字时间快捷方式可以用来替换通常用于指定时间的五个字段。 @ 字符用于标识 cron 的快捷方式。以下列表取自 crontab(5) 手册页,显示了快捷方式及其等效含义。

  • @reboot : 重启后运行一次。
  • @yearly : 每年运行一次,即 0 0 1 1 *
  • @annually : 每年运行一次,即 0 0 1 1 *
  • @monthly : 每月运行一次,即 0 0 1 * *
  • @weekly : 每周运行一次,即 0 0 * * 0
  • @daily : 每天运行一次,即 0 0 * * *
  • @hourly : 每小时运行一次,即 0 * * * *

这些快捷方式可以用于任何 crontab 文件,例如 /etc/cron.d 中的文件。

更多关于设置限制

我使用这些方法中的大多数来调度任务在我的计算机上运行。所有这些任务都需要以 root 权限运行。在我的经验中,普通用户真正需要 cron 任务的情况很少。一个案例是一位开发人员用户需要一个 cron 任务来启动开发实验室中的每日编译。

限制非 root 用户访问 cron 功能非常重要。但是,在某些情况下,用户需要设置任务在预先指定的时间运行,而 cron 可以允许他们这样做。许多用户不了解如何使用 cron 正确配置这些任务,他们会犯错误。这些错误可能是无害的,但更多时候,它们可能会引起问题。通过设置功能策略,使用户与系统管理员进行交互,单个 cron 任务不太可能干扰其他用户和其他系统功能。

可以设置可以分配给单个用户或组的总资源限制,但这将是另一篇文章的主题。

有关更多信息,croncrontabanacronanacrontabrun-parts 的手册页都包含关于 cron 系统如何工作的优秀信息和描述。

本文最初于 2017 年 11 月发布,并已更新以包含附加信息。

接下来阅读什么
David Both
David Both 是一位开源软件和 GNU/Linux 倡导者、培训师、作家和演讲者。自 1996 年以来,他一直从事 Linux 和开源软件方面的工作,自 1969 年以来一直从事计算机方面的工作。他是“系统管理员的 Linux 哲学”的坚定支持者和传播者。

9 条评论

很棒的文章,David。我在我的 .sh 文件中添加了两行,这些行被添加到自定义日志文件中。这样我就知道我的关键 cron 任务何时开始、结束以及完成任务花费了多长时间。

很棒的文章,David。一个小小的更正。 crontab -e 将使用任何默认编辑器。因此,如果您碰巧偏好 nano,例如,您可以将您的 EDITOR 环境变量设置为 nano。

Cron 绝对是一个好工具。但是,如果您需要进行更高级的调度,那么 Apache Airflow 非常适合。

Airflow 比 Cron 有许多优势。最重要的是:依赖项(让任务在其他任务之后运行)、漂亮的基于 Web 的概览、自动故障恢复和集中式调度器。缺点是您需要在 一台服务器上设置调度器和一些其他集中式组件,并在您想要在上面运行东西的每台机器上设置一个工作节点。

对于某些东西,您绝对想使用 Cron。但是,如果您发现 Cron 对于您的用例来说太有限,我建议您考虑 Airflow。

虽然我喜欢命令行?因为我是 Bash 脚本新手,如果 cron 有一个前端 GUI,我可以摆弄它直到我的命令行技能达到标准,那就太好了。但是话又说回来,走那条路?...只会让我变得懒惰。我想我将不得不艰难地学习这一切!!!哈哈!不过文章很棒!

嗨,David,
你写了一篇很棒的文章。非常感谢。我利用了 @reboot crontab 条目。使用 crontab 和 root。我运行以下命令。

@reboot /bin/dofstrim.sh

我想每周一次且仅一次为我的 SSD 驱动器运行 fstrim。
dofstrim.sh 是一个脚本,每周运行一次“fstrim”程序,而不管系统重启的次数。我碰巧有几个 Linux 系统共享一台计算机,每个系统都有一个包含该条目的 root crontab。由于我可能在一 天中或每周多次从 Linux 跳到 Linux,我的 dofstrim.sh 每周只运行一次 fstrim,而不管我启动哪个 Linux 系统。我利用所有 Linux 系统的公共分区,一个挂载为“/scratch”的分区和优秀的 Linux 命令行“date”程序。

dofstrim.sh 列表如下。

#!/bin/bash
# 运行 fstrim 一周一次或一天一次,而不是每次重启一次
#
# 使用 date 函数提取今天的日期号或周号
# 日期号范围是 1..366,周号是 1 到 53
#WEEKLY=0 #每天一次
WEEKLY=1 #每周一次
lockdir='/scratch/lock/'

if [[ WEEKLY -eq 1 ]]; then
dayno="$lockdir/dofstrim.weekno"
today=$(date +%V)
else
dayno=$lockdir/dofstrim.dayno
today=$(date +%j)
fi

prevval="000"

if [ -f "$dayno" ]
then
prevval=$(cat ${dayno} )
if [ x$prevval = x ];then
prevval="000"
fi
else
mkdir -p $lockdir
fi

if [ ${prevval} -ne ${today} ]
then
/sbin/fstrim -a
echo $today > $dayno
fi

我曾想过使用 anacron,但那样 fstrim 会被频繁运行,因为每个 linux 的 anacron 都会有类似的条目。
“date”程序生成日期号或周号,取决于 +%V 或 +%j

如果您使用 date 程序,则在月末最后一天运行报告很容易。使用来自 Linux 的 date 函数,如下所示

*/9 15 28-31 * * [ `date -d +'1 day' +\%d` -eq 1 ] && echo "明天是每月的第一天 今天(现在)是 `date`" >> /root/message

每天一次,从 28 日到 31 日,date 函数被执行。
如果 date +1day 的结果是每月的第一天,那么今天一定是月末的最后一天。

为什么不使用 crontab 来启动类似 Ansible Playbook 的东西,而不是简单的 bash 脚本?这些天更容易排除故障和管理。 :-)

知识共享许可协议本作品根据知识共享署名 - 相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.