wake-up-neo.com

为什么我的crontab无法正常工作,如何解决它?

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

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

答案为(为什么我的crontab不起作用,如何解决它?)。这解决了cron系统并突出显示了crontab。

246
Eric Leschinski

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


这是 社区Wiki ,如果您发现此答案有误或有其他信息,请进行编辑。


一,基本术语:

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

接下来,关于cron的教育:

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

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

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

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

crontab细节,如何制定命令:

Crontab命令用单行表示。您不能使用\将命令扩展为多行。井号(#)表示注释,这意味着cron会忽略该行上的任何内容。前导空格和空白行将被忽略。

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

Crontab文件有两种格式:

  • 用户crontab

    # 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
    
  • 系统范围内的/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
    

注意,后者需要一个用户名。该命令将以指定的用户身份运行。

该行的前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环境变量,例如.

[email protected]
1 2 * * * /path/to/your/command

自己捕获输出

您可以将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

看日志

Cron通过syslog记录其操作,该操作通常取决于/var/log/cron/var/log/syslog

如果需要,您可以使用以下代码过滤cron语句:.

grep CRON /var/log/syslog 

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

检查cron是否正在运行

如果cron没有运行,那么您的命令将不会被调度...

ps -ef | grep cron | grep -v grep

应该给你像

root    1224   1  0 Nov16 ?    00:00:03 cron

要么

root    2018   1  0 Nov14 ?    00:00:06 crond

如果不重启

/sbin/service cron start

要么

/sbin/service crond start

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

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

可用的环境变量可能非常有限。通常,您只会定义一些变量,例如$LOGNAME$HOME$PATH

特别要注意的是PATH被限制为/bin:/usr/bin绝大多数“我的cron脚本不起作用”问题是由这种限制性路径引起的。如果您的命令位于其他位置,则可以通过以下两种方法解决此问题:

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

    1 2 * * * /path/to/your/command
    
  2. 在crontab文件中提供合适的PATH

    PATH=/bin:/usr/bin:/path/to/something/else
    1 2 * * * command 
    

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

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

无论您执行的程序在文件系统上的哪个位置,在cron运行时,程序的当前工作目录均为用户的主目录。如果您访问程序中的文件,则在使用相对路径时必须考虑到这一点,或者(最好)在任何地方都使用完全限定的路径,这样可以避免所有人的混乱。

我的crontab中的最后一条命令未运行

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

检查crontab格式

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

我在/ etc/cron中放置了一个文件。{每小时,每天,每周,每月},它没有运行

  • 检查文件名没有扩展名,请参阅 run-parts
  • 确保文件具有执行权限。
  • 告诉系统执行脚本时要使用的内容(例如,将#!/bin/sh放在顶部)

与Cron日期相关的错误

如果您的日期最近是由用户或系统更新,时区或其他时间更改的,则crontab将开始出现异常行为,并显示异常的bug,有时可以正常工作,有时不能正常工作。当时间从下面改变时,这是crontab尝试尝试“做您想做的事”的尝试。更改小时后,“分钟”字段将失效。在这种情况下,仅星号将被接受。重新启动cron并重试一次,而无需连接到互联网(因此日期没有机会重置为时间服务器之一)。

百分号再次

为了强调有关百分号的建议,下面是cron如何处理百分号的示例:

# cron entry
* * * * * cat >$HOME/cron.out%foo%bar%baz

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

foo
bar
baz

当使用date命令时,这特别麻烦。确保逃脱百分号

* * * * * /path/to/command --day "$(date "+\%Y\%m\%d")"
352
Eric Leschinski

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

  • 归根所有
  • 只能由root写入
  • 群组或其他用户不可写
  • 具有名称不带点'。'或除'-'和'_'以外的任何其他特殊字符。

最后一个会伤害经常毫无戒心的用户;特别是这些文件夹之一中名为whatever.shmycron.pytestfile.pl等的任何脚本,将永远执行not

以我的经验,到目前为止,这一点一直是对Debian及其衍生工具不执行cronjob的最常见原因。

如有必要,请参见man cron了解更多详细信息。

25
wazoox

如果您的cronjobs停止工作,请检查您的密码是否尚未过期。因为一旦激活,所有cron作业都会停止。
/var/log/messages类似于以下显示用户身份验证问题的代码:

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

20
Munkeh72

时间表不定期

Cron一切都被认为是非常基本的调度程序,并且语法不容易允许管理员制定稍微不常见的调度程序。

考虑以下工作,通常将其解释为“每5分钟运行command

_*/5 * * * * /path/to/your/command
_

与:

_*/7 * * * * /path/to/your/command
_

其中不会总是每7分钟运行command

请记住 / 字符可以用来介绍一个步骤,但是这些步骤不会超出系列的结尾,例如_*/7_与分钟数_0-59_相匹配,即每7分钟匹配一次,即0,7,14,21,28,35,42,49,56但在一个小时到下一个之间在_00:56_之后,将有每批之间只有4分钟,一个新的系列将从_01:00_,_01:07_等开始(并且批处理不会在_01:03_,_01:10_,_01: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.
_

减少批次运行频率

而不是每7分钟运行一次批处理(这很难将其分解为多个批处理),而是每10分钟运行一次。

更频繁地启动批处理(但要防止多个批处理并发运行)

由于批处理运行时间会增加/波动,因此会生成许多奇怪的时间表,然后以一些额外的安全余量来调度批处理,以防止同一批批处理的后续运行重叠并发运行。

相反,请换个思路,创建一个cronjob,当先前的运行尚未完成时,它将正常地失败,但否则将运行。看到这个 Q&A

_* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job 
_

一旦先前的/ 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
_

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

_* * * * * /path/to/your/seven-minute-job
_

一个不同但又类似的问题是将批处理安排在每个月的第一个星期一(或第二个星期三)运行,等等。简单地将批处理安排在每个星期一运行,并且当日期不在两者之间时退出st 或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
_

然后您可以安全地(尝试)在每个星期一运行:

_0 0 * * 1 /path/to/your/first-monday-of-the-month-Housekeeping-job
_

不要使用cron

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

14
HBruijn

特定于PHP

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

php /bla/bla/something.php >> /var/logs/somelog-for-stdout.log

而且如果发生错误,则会将它们发送给您,但不会-请检查一下。

默认情况下,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。只有在您可以睡个好觉之后,所有内容才会发送到您根的邮件中,而不仅仅是被记录下来。

8
gaRex

添加我的答案 从这里开始 以获得完整性,并添加另一个可能有用的资源:

cron用户与您拥有不同的$PATH

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

$ ./certbot ... 

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

*/10 * * * * ./certbot ....

在这种情况下失败的原因是,cron用户的./是不同于登录用户的位置。也就是说,environment是不同的! PATH是environment的一部分,通常对于cron用户而言是不同的。使这个问题复杂化的是,对于所有 * nix 发行版,environmentcron都不相同,并且有 cron的多个版本

解决此特定问题的简单方法是在cron条目中为crontab用户提供完整的路径规范:

0 22 * * * /path/to/certbot .....

cron用户的environment是什么?

在某些情况下,我们可能需要了解系统上environment的完整cron规范(否则我们可能会感到好奇)。 environment用户的cron是什么,它与我们的用户有什么区别?此外,我们可能需要知道另一个environment用户的cron-例如rootroot用户的environment使用cron?一种学习方法是让cron告诉我们:

  1. 如下所示(或使用您选择的编辑器)在主目录(~/)中创建Shell脚本:
$ nano ~/envtst.sh
  1. 在针对您的系统/用户进行调整之后,在编辑器中输入以下内容:
#!/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
  1. 保存文件,退出编辑器并将文件权限设置为可执行文件。
$ chmod a+rx ~/envtst.sh
  1. 运行您刚创建的脚本,然后查看/home/you/envtst.sh.out中的输出。此输出将以您登录为$USER的身份显示当前环境:
$ ./envtst.sh $$ cat /home/you/envtst.sh.out
  1. 打开crontab进行编辑:
$ crontab -e -u root
  1. crontab的底部输入以下行:
* * * * *  /home/you/envtst.sh >> /home/you/envtst.sh.err 2>&1

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

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

crontab的计划条目当然是在man crontab中定义的,您应该阅读此内容。但是,阅读man crontab和了解进度表是两件事。进度表规范上的反复试验可能会变得很繁琐非常。幸运的是,有一个资源可以提供帮助: crontab专家。 。输入您的时间表规范,它将以纯英语解释时间表。

最后,不要冒着被限制为单个crontab条目的风险,因为这里有一项要安排的工作,因此有可能被此处的其他答案之一所困扰。您可以随意使用任意数量的crontab条目来获取所需的时间表。

0
Seamus