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


225

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

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

答案为“ 为什么我的crontab无法正常工作,如何解决它?可以在下面看到。这cron用crontab突出显示了系统。


2
这是crontab无法在AskUbuntu上运行的原因的巨大重复。
Dan Dascalescu

1
@DanDascalescu似乎Eric需要获得更多代表
我是最愚蠢的人

1
我刚加入Server Fault SE(所以只有101个代表),但是很乐意将此问题设为-1!这个问题只是为了得到代表吗?@IamtheMostStupidPerson完全同意您的意见
Holyprogrammer

这些十三岁的帕达瓦人中的西方意识形态既是教科书又是致盲的,就像超新星一样。要回答您的两个问题:是的,我是为销售代表做的,是的,埃里克(Eric)需要获得更多声誉。我还需要多少代表?更多。 youtu.be/IaDt9T7BF38?t=262
埃里克·莱斯钦斯基

Answers:


316

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


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


一,基本术语:

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

接下来,关于cron的教育:

系统上的每个用户都可以拥有自己的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文件crontab -e并保存时,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环境变量,例如

MAILTO=user@somehost.tld
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=/usr:/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中。{每小时,每天,每周,每月},它没有运行

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

与Cron日期相关的错误

如果您的日期最近是由用户或系统更新,时区或其他时间更改的,则crontab将开始出现异常行为,并显示出奇怪的错误,有时可以正常工作,有时无法正常工作。当时间从下面改变时,这是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")"

可能还想在“受限环境”部分中提到,如果您的cron任务由于找不到共享库而失败,则LD_LIBRARY_PATH可能还需要设置任何其他目录。
DavidJ 2015年

请注意,您甚至可以编写如下内容:35 1,5-23 / 2 * * * do_something而不是35,1,5,7,9,.. * * *另外,此crontab.guru会将您输入的内容转换为人类的语言。
丹尼斯·诺尔特

1
输出捕获对我不起作用,可能是由于sh shell所致。我认为这更便于移植: ... /path/to/your/command >/tmp/mycommand.log 2>&1
chus

这对我sudo apt-get install postfix
有用

作业是否还取决于文件的重量?因为我使用cron在python中运行了简单的hello世界,所以它起作用了。但是我的第二个代码有点沉重,通常可以运行,但是使用cron时,它不会向文件提供任何输出。
Devendra Bhat

22

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

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

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

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

man cron如有需要,请参阅以获取更多详细信息。


19

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

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


2
刚得到它(我的错误消息文件/ var / log / syslog)。就我而言,这是一个DigitalOcean框,在创建时,他们将根密码(可选)重置为另一个密码,并且显然直到您进入那里并对其进行更改之前,所有cron作业都不会运行。笨蛋 修复方法类似于sudo -u root passwd
rogerdpack '16

12

时间表不定期

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

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

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

与:

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

并不总是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.

减少批次运行频率

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

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

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

相反,请换个思路,创建一个cronjob,当先前的运行尚未完成时,它将正常地失败,但否则将运行。请参阅以下问答

* * * * * /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

一个不同但类似的问题是将批处理安排在每个月的第一个星期一(或第二个星期三)运行,等等。只需将批处理安排在每个星期一运行,然后在日期不在1 或7 到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

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


8

特定于PHP

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

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

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

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


2
该错误报告于2007年关闭,补丁程序的状态已添加到PHP 5.2+分支中。您确定这是必需的吗?我只是在PHP 5.4上尝试过,它似乎工作正常。(尽管PHP 4仍然需要它)。
Xeoncross 2014年

@Xeoncross看到答案的日期:)
gaRex 2014年

1
是的,这就是让我感到困惑的原因,因为您在2013年回答问题,而机票又回到了'07年。
Xeoncross 2014年

0

从此处添加我的答案以确保完整性,并添加另一个可能有用的资源:

cron用户$PATH与您的用户有所不同:

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

$ ./certbot ... 

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

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

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

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

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

是什么cron用户environment

在某些情况下,我们可能需要了解系统上的完整environment规范cron(否则我们可能会感到好奇)。什么是environment针对cron用户,并且它是如何与我们不同?此外,我们可能需要了解environment其他cron用户的- root例如... root用户在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将包含environment“ root cron用户” 的清单。知道之后,相应地调整您的crontab输入。

我无法在crontab输入中指定所需的时间表:

的时间表条目crontab当然是在中定义的man crontab,您应该阅读此内容。但是,阅读man crontab和理解时间表是两件事。日程安排规范中的反复试验会变得非常乏味。幸运的是,有一个资源可以提供帮助:crontab专家。。输入您的时间表规格,它将以纯英语解释时间表。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.