为什么我的 crontab 不起作用,我该如何解决它?

Eri*_*ski 313 linux cron

这是一个关于使用 cron 和 crontab的规范问题

您被定向到这里是因为社区相当确定您的问题的答案可以在下面找到。如果您的问题没有在下面得到解答,那么答案将帮助您收集有助于社区帮助您的信息。此信息应编辑为您的原始问题。

'为什么我的 crontab 不起作用,我该如何解决它?' 可以在下面看到。这解决了cron突出显示 crontab的系统。

Eri*_*ski 430

如何解决所有与 crontab 相关的困境/问题 (Linux)


这是一个社区维基,如果您发现此答案有任何不正确或有其他信息,请对其进行编辑。


一、基本术语:

  • cron(8)是执行预定命令的守护进程。
  • crontab(1)是用于修改用户 crontab(5) 文件的程序。
  • crontab(5)是包含 cron(8) 说明的每个用户文件。

接下来,关于cron的教育:

系统上的每个用户都可能有自己的 crontab 文件。root 和用户 crontab 文件的位置取决于系统,但它们通常在/var/spool/cron.

有一个系统范围的/etc/crontab文件,该/etc/cron.d目录可能包含 crontab 片段,这些片段也由 cron 读取和操作。一些 Linux 发行版(例如 Red Hat)也有/etc/cron.{hourly,daily,weekly,monthly}哪些目录,其中的脚本将每小时/天/周/月执行,具有 root 权限。

root 总是可以使用 crontab 命令;普通用户可能会或可能不会被授予访问权限。当您使用该命令编辑 crontab 文件crontab -e并保存它时,crond 会检查它的基本有效性,但不保证您的 crontab 文件格式正确。有一个名为的文件cron.deny将指定哪些用户不能使用 cron。该cron.deny文件的位置是系统相关的,可以删除,这将允许所有用户使用cron。

如果计算机未开机或 crond 守护程序未运行,并且运行命令的日期/时间已过,则 crond 将不会追赶并运行过去的查询。

crontab 细节,如何制定命令:

crontab 命令由一行表示。不能用于\将命令扩展到多行。散列 ( #) 符号表示注释,这意味着 cron 忽略该行上的任何内容。前导空格和空行将被忽略。

%在命令中使用百分比 ( ) 符号时要非常小心。除非它们被转义\%,否则它们将被转换为换行符,并且第一个非转义之后的所有内容都将%传递给您在 stdin 上的命令。

crontab 文件有两种格式:

  • 用户 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)
     # |  |  |  |  |
     # *  *  *  *  *   command to be executed
    
    Run Code Online (Sandbox Code Playgroud)
  • 系统范围/etc/crontab/etc/cron.d片段

     # 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)
     # |  |  |  |  |
     # *  *  *  *  * user-name  command to be executed
    
    Run Code Online (Sandbox Code Playgroud)

请注意,后者需要用户名。该命令将作为指定用户运行。

该行的前 5 个字段表示应运行命令的时间。您可以在时间规范中使用数字或适用的日/月名称。

  • 字段由空格或制表符分隔。
  • 逗号 ( ,) 用于指定列表,例如 1,4,6,8,表示在 1,4,6,8 处运行。
  • 范围用破折号 ( -)指定,并且可以与列表组合,例如 1-3,9-12,这意味着介于 1 和 3 之间,然后介于 9 和 12 之间。
  • /字符可用于引入一个步骤,例如 2/5,这意味着从 2 开始,然后每 5 (2,7,12,17,22...)。他们没有结束。
  • *字段中的星号 ( ) 表示该字段的整个范围(例如0-59分钟字段)。
  • 范围和步骤可以组合,例如*/2表示从相关字段的最小值开始,然后每 2 例如 0 表示分钟(0,2...58),1 表示月(1,3 ... 11)等。

调试 cron 命令

检查邮件!

默认情况下,cron 会将命令的任何输出邮寄给运行命令的用户。如果没有输出,就不会有邮件。如果您希望 cron 将邮件发送到不同的帐户,那么您可以在 crontab 文件中设置 MAILTO 环境变量,例如

MAILTO=user@somehost.tld
1 2 * * * /path/to/your/command
Run Code Online (Sandbox Code Playgroud)

自己捕获输出

您可以将 stdout 和 stderr 重定向到文件。捕获输出的确切语法可能因 shell cron 使用的不同而有所不同。以下是将所有输出保存到文件的两个示例/tmp/mycommand.log

1 2 * * * /path/to/your/command &>/tmp/mycommand.log
1 2 * * * /path/to/your/command >/tmp/mycommand.log 2>&1
Run Code Online (Sandbox Code Playgroud)

查看日志

Cron 通过 syslog 记录其操作,该日志(取决于您的设置)通常会转到/var/log/cron/var/log/syslog

如果需要,您可以使用例如过滤 cron 语句

grep CRON /var/log/syslog 
Run Code Online (Sandbox Code Playgroud)

现在我们已经了解了 cron 的基础知识、文件在哪里以及如何使用它们,让我们看看一些常见问题。

检查 cron 是否正在运行

如果 cron 没有运行,那么你的命令将不会被安排......

ps -ef | grep cron | grep -v grep
Run Code Online (Sandbox Code Playgroud)

应该给你类似的东西

root    1224   1  0 Nov16 ?    00:00:03 cron
Run Code Online (Sandbox Code Playgroud)

或者

root    2018   1  0 Nov14 ?    00:00:06 crond
Run Code Online (Sandbox Code Playgroud)

如果没有重启

/sbin/service cron start
Run Code Online (Sandbox Code Playgroud)

或者

/sbin/service crond start
Run Code Online (Sandbox Code Playgroud)

可能还有其他方法;使用您的发行版提供的内容。

cron 在受限环境中运行您的命令。

可用的环境变量可能非常有限。通常情况下,你只会得到定义了若干变量,如$LOGNAME$HOME$PATH

特别值得注意的PATH是 仅限于/bin:/usr/bin绝大多数“我的 cron 脚本不起作用”的问题都是由这个限制性路径引起的。如果您的命令位于不同的位置,您可以通过以下几种方式解决此问题:

  1. 提供命令的完整路径。

     1 2 * * * /path/to/your/command
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在 crontab 文件中提供合适的 PATH

     PATH=/bin:/usr/bin:/path/to/something/else
     1 2 * * * command 
    
    Run Code Online (Sandbox Code Playgroud)

如果您的命令需要其他环境变量,您也可以在 crontab 文件中定义它们。

cron 使用 cwd == $HOME 运行您的命令

无论您执行的程序驻留在文件系统的哪个位置,cron 运行时程序的当前工作目录都将是用户的主目录。如果您访问程序中的文件,则在使用相对路径时需要考虑这一点,或者(最好)只是在任何地方都使用完全限定的路径,这样可以避免每个人都非常困惑。

我的 crontab 中的最后一个命令没有运行

Cron 通常要求命令以新行结束。编辑您的 crontab;转到包含最后一个命令的行的末尾并插入一个新行(按 Enter)。

检查 crontab 格式

您不能将用户 crontab 格式的 crontab 用于 /etc/crontab 或 /etc/cron.d 中的片段,反之亦然。用户格式的 crontab 在行的第 6 位不包含用户名,而系统格式的 crontab 包含用户名并以该用户身份运行命令。

我在 /etc/cron.{hourly,daily,weekly,monthly} 中放了一个文件,但它没有运行

  • 检查文件名是否没有扩展名,请参阅运行部分
  • 确保文件具有执行权限。
  • 告诉系统在执行脚本时使用什么(例如放在#!/bin/sh顶部)

Cron 日期相关的错误

如果您的日期最近因用户或系统更新、时区或其他原因而更改,那么 crontab 将开始出现异常行为并出现奇怪的错误,有时有效,有时无效。这是 crontab 尝试在时间从它下面改变时“做你想做的事”。更改小时后,“分钟”字段将失效。在这种情况下,只接受星号。重新启动 cron 并在不连接到 Internet 的情况下重试(因此日期没有机会重置为其中一个时间服务器)。

百分号,再次

为了强调关于百分号的建议,这里有一个 cron 对它们做了什么的例子:

# cron entry
* * * * * cat >$HOME/cron.out%foo%bar%baz
Run Code Online (Sandbox Code Playgroud)

将创建包含 3 行的 ~/cron.out 文件

foo
bar
baz
Run Code Online (Sandbox Code Playgroud)

这在使用date命令时特别具有侵入性。一定要避开百分号

* * * * * /path/to/command --day "$(date "+\%Y\%m\%d")"
Run Code Online (Sandbox Code Playgroud)

小心须藤

以非 root 用户身份运行时,

crontab -e
Run Code Online (Sandbox Code Playgroud)

将打开用户的 crontab,而

sudo crontab -e
Run Code Online (Sandbox Code Playgroud)

将打开 root 用户的 crontab。不建议在 cron 作业中运行 sudo 命令,因此如果您尝试在用户的 cron 中运行 sudo 命令,请尝试将该命令移动到 root 的 cron 并从命令中删除 sudo。

  • 在花了几个小时找出 cron 不工作的原因后,“Cron 通常要求命令以新行终止。” (4认同)

waz*_*oox 30

Debian Linux 及其衍生产品(Ubuntu、Mint 等)有一些特性可能会阻止您的 cron 作业执行;特别是,在文件中/etc/cron.d/etc/cron.{hourly,daily,weekly,monthly}必须:

  • 由 root 拥有
  • 只能被 root 写入
  • 组或其他用户不可写
  • 有一个没有任何点的名字“。” 或任何其他特殊字符,但 '-' 和 '_' 。

最后一个经常伤害毫无戒心的用户;在这些文件夹命名为一个特定的任何脚本whatever.shmycron.pytestfile.pl等会不会被执行,直到永远。

根据我的经验,这一点是迄今为止在 Debian 和衍生产品上不执行 cronjob 的最常见原因。

man cron如有必要,请查看更多详细信息。


小智 22

如果您的 cronjob 停止工作,请检查您的密码是否已过期。因为一旦过期,所有 cron 作业都会停止。
将有/var/log/messages类似于下面的消息,显示用户身份验证的问题:

(username) FAILED to authorize user with PAM (Authentication token is no longer valid; new one required)

  • 刚刚得到这个(错误消息文件 /var/log/syslog 对我来说)。在我的例子中,一个 DigitalOcean 盒子,在创建时,他们将 root 密码(可选)重置为另一个密码,显然,在你进入那里并更改它之前,所有的 cron 作业都不会运行。无赖。修复类似于`sudo -u root passwd` (3认同)

HBr*_*ijn 16

不寻常和不规则的时间表

Cron 被认为是一个非常基本的调度程序,其语法不允许管理员轻松制定稍微不常见的调度程序。

考虑以下通常被解释为command每 5 分钟运行一次”的作业

*/5 * * * * /path/to/your/command
Run Code Online (Sandbox Code Playgroud)

相对:

*/7 * * * * /path/to/your/command
Run Code Online (Sandbox Code Playgroud)

并不总是command每 7 分钟运行一次

请记住,该/字符可用于引入一个步骤,但该步骤不会超出系列的末尾,例如*/7从分钟开始每 7 分钟匹配一次,0-59 即 0、7、14、21、28、35、42、49, 56,但一小时,下一之间会有批次之间有4分钟后, 00:56在一系列新的开始 01:0001:07等(和批次将不会运行上01:0301:1001:17等)。


该怎么办?

创建多个批次

与其创建单个 cron 作业,不如创建多个批处理,将结果组合到所需的计划中。

例如,每 40 分钟(00:00、00:40、01:20、02:00 等)运行一个批次,创建两个批次,一个在偶数小时运行两次,第二个只在奇数小时运行:

# The following lines create a batch that runs every 40 minutes i.e.

# runs on   0:00, 0:40,        02:00, 02:40         04:00 etc to 22:40
0,40 */2 * * * /path/to/your/command

# runs on               01:20,               03:20,       etc to 23:20
20 1/2 * * * /path/to/your/command

# Combined: 0:00, 0:40, 01:20, 02:00, 02:40, 03:20, 04:00 etc.
Run Code Online (Sandbox Code Playgroud)

减少运行批次的频率

与其每 7 分钟运行一次批次(这是一个很难分解成多个批次的时间表),不如每 10 分钟运行一次。

更频繁地启动您的批次(但防止多个批次同时运行)

许多奇怪的调度会演变,因为批处理运行时间增加/波动,然后批处理被调度有一点额外的安全余量,以防止同一批次的后续运行重叠和并发运行。

相反,换一种方式思考并创建一个 cronjob,它会在上一次运行尚未完成时正常失败,但否则会运行。请参阅此问答

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job 
Run Code Online (Sandbox Code Playgroud)

一旦 /usr/local/bin/frequent_cron_job 的前一次运行完成,这几乎会立即开始新的运行。

更频繁地启动您的批次(但在条件不合适时优雅地退出)

由于 cron 语法有限,您可能决定在批处理作业本身(或围绕现有批处理作业的包装器脚本中)放置更复杂的条件和逻辑。这允许您利用您最喜欢的脚本语言的高级功能,对您的代码进行注释,并防止在 crontab 条目本身中出现难以阅读的结构。

在 bash 中,它seven-minute-job看起来像这样:

#!/bin/bash
# seven-minute-job
# This batch will only run when 420 seconds (7 min) have passed
# since the file /tmp/lastrun was either created or updated

if [ ! -f /tmp/lastrun ] ; then
    touch /tmp/lastrun
fi

if [ $(( $(date +%s) - $(date -r /tmp/lastrun +%s) )) -lt 420 ] ; then
     # The minimum interval of 7 minutes between successive batches hasn't passed yet.
    exit 0
fi

####  Start running your actual batch job below
  
/path/to/your/command

#### actual batch job is done, now update the time stamp
date > /tmp/lastrun
#EOF
Run Code Online (Sandbox Code Playgroud)

然后您可以安全地(尝试)每分钟运行一次:

* * * * * /path/to/your/seven-minute-job
Run Code Online (Sandbox Code Playgroud)

一个不同,但类似的问题会安排一批在每月的第一个星期一(或第二个星期三)等运行只需调度运行每周一批次和退出时的日期既不是1间或7和星期几不是星期一。

#!/bin/bash
# first-monday-of-the-month-housekeeping-job

# exit if today is not a Monday (and prevent locale issues by using the day number) 
if [ $(date +%u) != 1 ] ; then
  exit 0
fi

# exit if today is not the first Monday
if [ $(date +%d) -gt 7 ] ; then
  exit 0
fi

####  Start running your actual batch job below
  
/path/to/your/command

#EOF
Run Code Online (Sandbox Code Playgroud)

然后您可以安全地(尝试)每周一运行:

0 0 * * 1 /path/to/your/first-monday-of-the-month-housekeeping-job
Run Code Online (Sandbox Code Playgroud)

不要使用 cron

如果您的需求很复杂,您可以考虑使用更高级的产品,该产品旨在运行复杂的计划(分布在多个服务器上)并支持触发器、作业依赖关系、错误处理、重试和重试监控等。行业术语是“企业“作业调度和/或“工作负载自动化”。


小智 8

特定于 PHP 的

如果您有一些 cron 工作,例如:

php /bla/bla/something.php >> /var/logs/somelog-for-stdout.log
Run Code Online (Sandbox Code Playgroud)

如果出现错误,他们会被发送给您,但他们不会 - 检查这个。

PHP 默认不向 STDOUT 发送错误。@see https://bugs.php.net/bug.php?id=22839

要解决此问题,请在 cli 的 php.ini 或您的行中(或在您的 PHP 的 bash 包装器中)添加这些:

  • --define display_startup_errors=1
  • --define display_errors='stderr'

第一个设置将允许您拥有像“Memory oops”和第二个这样的致命错误——将它们全部重定向到 STDERR。只有在您可以睡个好觉之后,所有内容才会被发送到您的 root 的邮件中,而不仅仅是登录。

  • 该错误报告于 2007 年关闭,补丁状态已添加到 PHP 5.2+ 分支。你确定需要这个吗?我刚刚尝试了 PHP 5.4,它似乎工作正常。(不过 PHP 4 仍然需要它)。 (2认同)

Sea*_*mus 5

为了完整起见,从这里添加我的答案,并添加另一个可能有用的资源:

用户的情况与您cron不同:$PATH

用户在使用条目时经常遇到的问题crontab是他们忘记了条目的运行方式与登录用户的cron运行方式不同。environment例如,用户在其$HOME目录中创建程序或脚本,并输入以下命令来运行它:

$ ./certbot ... 
Run Code Online (Sandbox Code Playgroud)

该命令从他的命令行完美运行。然后用户将该命令添加到他的 中crontab,但发现这不起作用:

*/10 * * * * ./certbot ....
Run Code Online (Sandbox Code Playgroud)

在这种情况下失败的原因是用户的位置与登录用户的./位置不同。cron也就是说,environment不一样!PATH 是 的一部分environment,并且对于用户来说通常是不同的cron。让这个问题变得复杂的是,所有*nixenvironment发行版的forcron都不相同,并且有多个版本cron

对于这个特定问题的一个简单解决方案是在条目中为用户提供cron完整的路径规范crontab

$ ./certbot ... 
Run Code Online (Sandbox Code Playgroud)

cron用户的是什么environment

在某些情况下,我们可能需要了解系统environment的完整规范(或者我们可能只是好奇)。对于用户来说是什么?它与我们的有何不同?此外,我们可能需要知道另一个用户的 -例如......用户正在使用什么?了解这一点的一种方法是要求告诉我们cronenvironmentcronenvironmentcronrootrootenvironmentcroncron

  1. 在您的主目录 ( ) 中创建一个 shell 脚本,~/如下所示(或使用您选择的编辑器):
$ nano ~/envtst.sh
Run Code Online (Sandbox Code Playgroud)
  1. 针对您的系统/用户进行调整后,在编辑器中输入以下内容:
*/10 * * * * ./certbot ....
Run Code Online (Sandbox Code Playgroud)
  1. 保存文件,退出编辑器并将文件权限设置为可执行。
0 22 * * * /path/to/certbot .....
Run Code Online (Sandbox Code Playgroud)
  1. 运行您刚刚创建的脚本,并查看 中的输出/home/you/envtst.sh.out。此输出将显示您$USER登录时的当前环境:
$ nano ~/envtst.sh
Run Code Online (Sandbox Code Playgroud)
  1. 打开您的crontab进行编辑:
#!/bin/sh 
/bin/echo "env report follows for user "$USER >> /home/you/envtst.sh.out 
/usr/bin/env >> /home/you/envtst.sh.out 
/bin/echo "env report for user "$USER" concluded" >> /home/you/envtst.sh.out
/bin/echo " " >> /home/you/envtst.sh.out
Run Code Online (Sandbox Code Playgroud)
  1. 在您的底部输入以下行crontab
$ chmod a+rx ~/envtst.sh
Run Code Online (Sandbox Code Playgroud)

解答:输出文件将包含“root cron 用户”/home/you/envtst.sh.out的列表。environment一旦您知道了这一点,请crontab相应地调整您的条目。

我无法在条目中指定我需要的时间表crontab

crontab当然,计划条目是在 中定义的man crontab,您应该阅读此内容。然而,阅读man crontab和理解时间表是两件不同的事情。时间表规范上的反复试验可能会变得非常乏味。幸运的是,有一个资源可以提供帮助:crontab guru。。输入您的日程安排说明,它将以简单的英语解释日程安排。

最后,冒着与这里的其他答案之一重复的风险,不要陷入这样的想法,crontab因为你有一项工作要安排,所以你只能进入一个条目。您可以根据需要自由使用任意数量的crontab条目来获得所需的时间表。

  • 对我来说,不同的环境总是会导致 cron 出现问题。这个答案应该得到更多的支持。 (2认同)