为什么crontab脚本不起作用?

通常, crontab脚本不按计划或按预期执行。 原因很多:

  1. 错误的crontab表示法
  2. 权限问题
  3. 环境变量

此社区wiki旨在聚合crontab脚本未按预期执行的主要原因。 在单独的答案中写下每个原因。

请为每个答案包含一个原因 – 有关其未执行原因的详细信息 – 并修复原因。

请仅编写特定于cron的问题,例如,从shell执行预期但由cron错误执行的命令。

不同的环境

Cron将一组最小的环境变量传递给您的工作。 要查看差异,请添加如下虚拟作业:

  * * * * * env> /tmp/env.output 

等待创建/tmp/env.output ,然后再次删除该作业。 现在将/tmp/env.output的内容与常规终端中env run的输出进行比较。

这里常见的“问题”是PATH环境变量不同。 也许您的cron脚本使用/opt/someApp/bin的命令somecommand ,您已将其添加到/etc/environment PATH中? cron忽略该文件中的PATH ,因此当您使用cron运行时,从脚本运行somecommand将失败,但在终端中运行时会工作。 值得注意的是, /etc/environment中的变量将被传递给cron作业,而不是cron专门设置的变量,例如PATH

要解决这个问题,只需在脚本顶部设置自己的PATH变量即可。 例如

 #!/bin/bash PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin # rest of script follows 

有些人宁愿只使用绝对路径来代替所有命令。 我建议反对。 考虑如果要在不同的系统上运行脚本会发生什么,并且在该系统上,命令位于/opt/someAppv2.2/bin 。 您必须使用/opt/someAppv2.2/bin替换/opt/someApp/bin而不是仅在脚本的第一行进行小编辑。

您还可以在crontab文件中设置PATH变量,该变量将应用于所有cron作业。 例如

 PATH=/opt/someApp/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin 15 1 * * * backupscript --incremental /home /root 

我的热门问题:如果您忘记在crontab文件的末尾添加换行符。 换句话说,crontab文件应以空行结束。

下面是本期手册页的相关部分( man crontab然后跳到最后):

  Although cron requires that each entry in a crontab end in a newline character, neither the crontab command nor the cron daemon will detect this error. Instead, the crontab will appear to load normally. However, the command will never run. The best choice is to ensure that your crontab has a blank line at the end. 4th Berkeley Distribution 29 December 1993 CRONTAB(1) 

Cron守护程序未运行。 几个月前我真的搞砸了。

类型:

 pgrep cron 

如果你看不到号码,那么cron没有运行。 sudo /etc/init.d/cron start可以用来启动cron。

编辑:不是通过/etc/init.d调用init脚本,而是使用服务实用程序,例如

 sudo service cron start 

cron.hourly/等中的脚本文件名不应包含点( . ),否则run-parts将跳过它们。

见运行部分(8):

  If neither the --lsbsysinit option nor the --regex option is given then the names must consist entirely of upper and lower case letters, dig‐ its, underscores, and hyphens. If the --lsbsysinit option is given, then the names must not end in .dpkg-old or .dpkg-dist or .dpkg-new or .dpkg-tmp, and must belong to one or more of the following namespaces: the LANANA-assigned namespace (^[a-z0-9]+$); the LSB hierarchical and reserved namespaces (^_?([a-z0-9_.]+-)+[a-z0-9]+$); and the Debian cron script namespace (^[a-zA-Z0-9_-]+$). 

因此,如果您在cron.daily/目录中有cron脚本backup.shanalyze-logs.pl ,则最好删除扩展名。

在许多环境中,cron使用sh执行命令,而许多人则认为它将使用bash

建议为失败的命令测试或修复此问题:

  • 尝试在sh运行命令以查看它是否有效
  • 将命令包装在bash子shell中以确保它在bash中运行:
    bash -c "mybashcommand"
  • 通过在crontab顶部设置shell,告诉cron在bash中运行所有命令:
    SHELL=/bin/bash
  • 如果命令是脚本,请确保脚本包含shebang:
    #!/bin/bash

我在时区遇到了一些问题。 Cron正在以新的安装时区运行。 解决方案是重启cron:

 sudo service cron restart 

如果您的crontab命令中包含%符号,则cron会尝试解释它。 因此,如果您使用任何带有%命令(例如date命令的格式规范),则需要将其转义。

这和其他好的问题在这里:
http://www.pantz.org/software/cron/croninfo.html

绝对路径应该用于脚本:

例如,应该使用/bin/grep而不是grep

 # mh dom mon dow command 0 0 * * * /bin/grep ERROR /home/adam/run.log &> /tmp/errors 

代替:

 # mh dom mon dow command 0 0 * * * grep ERROR /home/adam/run.log &> /tmp/errors 

这特别棘手,因为从shell执行时,相同的命令将起作用。 原因是cron没有与用户相同的PATH环境变量。

用户密码也可能已过期。 即使root的密码也可以过期。 你可以tail -f /var/log/cron.log ,你会看到cron失败,密码已过期。 您可以通过执行以下操作将密码设置为永不过期: passwd -x -1

在某些系统(Debian,Ubuntu)中,默认情况下不启用cron日志记录。 在/etc/rsyslog.conf/etc/rsyslog.d/50-default.conf中:

 # cron.* /var/log/cron.log 

应编辑( sudo nano /etc/rsyslog.conf )取消注释:

 cron.* /var/log/cron.log 

之后,您需要重启rsyslog

 /etc/init.d/rsyslog restart 

要么

 service rsyslog restart 

来源: 在Debian Linux中启用crontab日志记录

在某些系统(Ubuntu)中,默认情况下不启用cron的单独日志记录文件,但cron相关日志出现在syslog文件中。 一个人可以使用

 cat /var/log/syslog | grep cron 

查看与cron相关的消息。

Cron正在调用一个不可执行的脚本。

通过运行chmod +x /path/to/scrip ,脚本变为可执行文件并应解决此问题。

如果你的cronjob调用GUI-apps,你需要告诉他们应该使用什么DISPLAY。

示例:使用cron启动Firefox。

你的脚本应该在某处包含export DISPLAY=:0

我担心权限问题很常见。

请注意,一个常见的解决方法是使用root的crontab执行所有操作,有时这是一个非常不好的主意。 设置适当的权限绝对是一个被忽视的问题。

不安全的cron表权限

如果cron表的权限不安全,则会被拒绝

 sudo service cron restart grep -i cron /var/log/syslog|tail -2 2013-02-05T03:47:49.283841+01:00 ubuntu cron[49906]: (user) INSECURE MODE (mode 0600 expected) (crontabs/user) 

问题解决了

 # correct permission sudo chmod 600 /var/spool/cron/crontabs/user # signal crond to reload the file sudo touch /var/spool/cron/crontabs 

脚本对位置敏感。 这与始终在脚本中使用绝对路径有关,但并不完全相同。 您的cron作业可能需要在运行之前cd到特定目录,例如Rails应用程序上的rake任务可能需要在Rake的应用程序根目录中才能找到正确的任务,更不用说相应的数据库配置等。

所以一个crontab条目

23 3 * * * /usr/bin/rake db:session_purge RAILS_ENV=production

会更好的

 23 3 * * * cd / var / www / production / current && / usr / bin / rake db:session_purge RAILS_ENV = production

或者,保持crontab条目更简单,更脆弱:

23 3 * * * /home//scripts/session-purge.sh

使用/home//scripts/session-purge.sh的以下代码:

 cd / var / www / production / current
 / usr / bin / rake db:session_purge RAILS_ENV = production

从一个crontab文件移动到另一个crontab文件时,过去运行的 Crontab规范可能会中断。 有时原因是您已将规范从系统crontab文件移动到用户crontab文件,反之亦然。

用户的crontab文件(/ var / spool / cron / username或/ var / spool / cron / crontabs / username)与系统crontabs( /etc/crontab/etc/cron.d的文件)之间的cron作业规范格式不同/etc/cron.d )。

系统crontabs在命令运行之前有一个额外的字段“用户”。

这将导致错误陈述像george; command not found这样的事情george; command not found 将命令从/etc/crontab/etc/cron.d的文件移动到用户的crontab文件中时george; command not found命令。

相反,当发生相反的情况时,cron会发送错误,例如/usr/bin/restartxyz is not a valid username或类似/usr/bin/restartxyz is not a valid username

cron脚本使用–verbose选项调用命令

我有一个cron脚本失败,因为我在键入脚本时处于自动驾驶状态,并且我包含了–verbose选项:

 #!/bin/bash some commands tar cvfz /my/archive/file.tar.gz /my/shared/directory come more commands 

从shell执行时脚本运行正常,但从crontab运行时失败,因为从shell运行时详细输出转到stdout,但从crontab运行时无处可去。 轻松修复以删除’v’:

 #!/bin/bash some commands tar cfz /my/archive/file.tar.gz /my/shared/directory some more commands 

最常见的原因我看到cron在错误陈述的时间表中失败了。 需要练习指定安排在晚上11:15的作业为30 23 * * *而不是* * 11 15 *11 15 * * * 。 午夜之后的工作日也会变得混乱MF午夜后2-6不是1-5 。 具体日期通常是一个问题,因为我们很少使用它们* * 3 1 *不是3月3日。

如果您使用不支持的选项(如时间规范中的2/3使用不同平台的工作也会导致失败。 这是一个非常有用的选项,但不是普遍可用的。 我还遇到了像1-51,3,5这样的问题列表。

使用不合格的路径也会造成问题。 默认路径通常是/bin:/usr/bin因此只运行标准命令。 这些目录通常没有所需的命令。 这也会影响使用非标准命令的脚本。 其他环境变量也可能丢失。

彻底解决现有的crontab问题已经给我带来了麻烦。 我现在从文件副本加载。 如果它被破坏,可以使用crontab -l从现有的crontab中恢复。 我将crontab的副本保存在〜/ bin中。 它在整个评论中以# EOF行结束。 每天从crontab条目重新加载,如:

  #!的/ usr / bin中/ crontab中
 #重新加载此crontab
 #
 54 12 * * * $ {HOME} / bin / crontab 

上面的reload命令依赖于可执行的crontab,其中包含运行crontab的bang路径。 某些系统需要在命令中运行crontab并指定文件。 如果目录是网络共享的,那么我经常使用crontab.$(hostname)作为文件的名称。 这最终将纠正在错误的服务器上加载错误的crontab的情况。

使用该文件提供了crontab应该是什么的备份,并允许自动退出临时编辑(我使用crontab -e的唯一时间)。 有可用的标题有助于正确获取调度参数。 当缺乏经验的用户编辑crontab时我添加了它们。

很少,我遇到了需要用户输入的命令。 这些在crontab下失败,尽管有些可以使用输入重定向。

如果您通过SSH密钥访问帐户,则可以登录该帐户,但不会注意到帐户上的密码已被锁定(例如,由于密码尝试过期或密码无效)

如果系统使用PAM并且帐户被锁定,则可以阻止其cronjob运行。 (我在Solaris上测试了这个,但在Ubuntu上没有测试过)

你可以在/ var / adm / messages中找到这样的消息:

 10月24日07:51:00 mybox cron [29024]:[ID 731128 auth.notice] pam_unix_account:cron尝试从本地主机validation锁定的帐户myuser
 10月24日07:52:00 mybox cron [29063]:[ID 731128 auth.notice] pam_unix_account:cron尝试从本地主机validation锁定的帐户myuser
 10月24日07:53:00 mybox cron [29098]:[ID 731128 auth.notice] pam_unix_account:cron尝试从本地主机validation锁定的帐户myuser
 10月24日07:54:00 mybox cron [29527]:[ID 731128 auth.notice] pam_unix_account:cron尝试从本地主机validation锁定的帐户myuser

你需要做的就是运行:

 #passwd -u 

以root身份解锁帐户,crontab应该再次运行。

如果您有这样的命令:

 * * * * * /path/to/script >> /tmp/output 

并且它不起作用,你看不到任何输出,它可能不一定意味着cron不工作。 脚本可能会被破坏,输出将转到stderr ,而不会传递给/ tmp / output。 通过捕获此输出来检查不是这种情况:

 * * * * * /path/to/script >> /tmp/output 2>&1 

看看这是否有助于解决您的问题。

===docker警报===

如果你正在使用docker工具,

我认为添加我无法让cron在后台运行是合适的。

要在容器内运行cron作业,我使用了supervisor并运行了cron -f和其他进程。

编辑:另一个问题 – 在使用HOST网络运行容器时,我也没有设法使其工作。 另请参阅此问题: https : //github.com/phusion/baseimage-docker/issues/144

在我的情况下,cron和crontab有不同的所有者。

不工作我有这个:

 User@Uva ~ $ ps -ef | grep cron | grep -v grep User 2940 7284 pty1 19:58:41 /usr/bin/crontab SYSTEM 11292 636 ? 22:14:15 /usr/sbin/cro 

基本上我必须运行cron-config并正确回答问题。 有一点我需要为我的“用户”帐户输入我的Win7用户密码。 从我做的阅读,看起来这是一个潜在的安全问题,但我是一个家庭网络的唯一管理员,所以我认为这是好的。

这是让我走的命令序列:

 User@Uva ~ $ cron-config The cron daemon can run as a service or as a job. The latter is not recommended. Cron is already installed as a service under account LocalSystem. Do you want to remove or reinstall it? (yes/no) yes OK. The cron service was removed. Do you want to install the cron daemon as a service? (yes/no) yes Enter the value of CYGWIN for the daemon: [ ] ntsec You must decide under what account the cron daemon will run. If you are the only user on this machine, the daemon can run as yourself. This gives access to all network drives but only allows you as user. To run multiple users, cron must change user context without knowing the passwords. There are three methods to do that, as explained in http://cygwin.com/cygwin-ug-net/ntsec.html#ntsec-nopasswd1 If all the cron users have executed "passwd -R" (see man passwd), which provides access to network drives, or if you are using the cyglsa package, then cron should run under the local system account. Otherwise you need to have or to create a privileged account. This script will help you do so. Do you want the cron daemon to run as yourself? (yes/no) no Were the passwords of all cron users saved with "passwd -R", or are you using the cyglsa package ? (yes/no) no Finding or creating a privileged user. The following accounts were found: 'cyg_server' . This script plans to use account cyg_server. Do you want to use another privileged account name? (yes/no) yes Enter the other name: User Reenter: User Account User already exists. Checking its privileges. INFO: User is a valid privileged account. INFO: The cygwin user name for account User is User. Please enter the password for user 'User': Reenter: Running cron_diagnose ... ... no problem found. Do you want to start the cron daemon as a service now? (yes/no) yes OK. The cron daemon is now running. In case of problem, examine the log file for cron, /var/log/cron.log, and the Windows event log (using /usr/bin/cronevents) for information about the problem cron is having. Examine also any cron.log file in the HOME directory (or the file specified in MAILTO) and cron related files in /tmp. If you cannot fix the problem, then report it to cygwin@cygwin.com. Please run the script /usr/bin/cronbug and ATTACH its output (the file cronbug.txt) to your e-mail. WARNING: PATH may be set differently under cron than in interactive shells. Names such as "find" and "date" may refer to Windows programs. User@Uva ~ $ ps -ef | grep cron | grep -v grep User 2944 11780 ? 03:31:10 /usr/sbin/cron User 2940 7284 pty1 19:58:41 /usr/bin/crontab User@Uva ~ $ 

我正在编写一个安装shell脚本,该脚本创建另一个脚本来清除数据库中的旧事务数据。 作为任务的一部分,当数据库负载较低时,必须将每日cron作业配置为在任意时间运行。

我用cron schedule,username和命令创建了一个文件mycronjob ,并将其复制到/etc/cron.d目录。 我的两个陷阱:

  1. mycronjob文件必须由root拥有才能运行
  2. 我不得不将文件的权限设置为644 – 664不会运行。

权限问题将出现在/var/log/syslog ,类似于:

 Apr 24 18:30:01 ip-11-22-33-44 cron[40980]: (*system*) INSECURE MODE (group/other writable) (/etc/crontab) Apr 24 18:30:01 ip-11-22-33-44 cron[40980]: (*system*) INSECURE MODE (group/other writable) (/etc/cron.d/user) 

第一行引用/etc/crontab文件,后一行引用我放在/etc/cront.d下的文件。

以crontab的方式写的行不明白。 它需要正确编写。 这是CrontabHowTo 。

Cron守护程序可能正在运行,但实际上并没有运行。 尝试重启cron:

 sudo /etc/init.d/cron restart 

通过“crontab -e”用一行中的用户名参数写入cron。 I’ve seen examples of users (or sysadmins) writing their shell scripts and not understanding why they don’t automate. The “user” argument exists in /etc/crontab, but not the user-defined files. so, for example, your personal file would be something like:

 # mh dom mon dow command * * */2 * * /some/shell/script 

whereas /etc/crontab would be:

 # mh dom mon dow user command * * */2 * * jdoe /some/shell/script 

So, why would you do the latter? Well, depending on how you want to set your permissions, this can become very convoluted. I’ve written scripts to automate tasks for users who don’t understand the intricacies, or don’t want to bother with the drudgery. By setting permissions to --x------ , I can make the script executable without them being able to read (and perhaps accidentally change) it. However, I might want to run this command with several others from one file (thus making it easier to maintain) but make sure file output is assigned the right owner. Doing so (at least in Ubuntu 10.10) breaks on both the inability to read the file as well as execute, plus the afore-mentioned issue with putting periods in /etc/crontab (which, funnily enough, causes no error when going through crontab -e ).

As an example, I’ve seen instances of sudo crontab -e used to run a script with root permissions, with a corresponding chown username file_output in the shell script. Sloppy, but it works. IMHO, The more graceful option is to put it in /etc/crontab with username declared and proper permissions, so file_output goes to the right place and owner.

Building off what Aaron Peart mentioned about verbose mode, sometimes scripts not in verbose mode will initialize but not finish if the default behavior of an included command is to output a line or more to the screen once the proc starts. For example, I wrote a backup script for our intranet which used curl, a utility that downloads or uploads files to remote servers, and is quite handy if you can only access said remote files through HTTP. Using ‘curl http://something.com/somefile.xls ‘ was causing a script I wrote to hang and never complete because it spits out a newline followed by a progress line. I had to use the silent flag (-s) to tell it not to output any information, and write in my own code to handle if the file failed to download.

Although you can define environment variables in your crontable, you’re not in a shell script. So constructions like the following won’t work:

 SOME_DIR=/var/log MY_LOG_FILE=${SOME_LOG}/some_file.log BIN_DIR=/usr/local/bin MY_EXE=${BIN_DIR}/some_executable_file 0 10 * * * ${MY_EXE} some_param >> ${MY_LOG_FILE} 

This is because variables are not interpreted in the crontable: all values are taken litterally. And this is the same if you omit the brackets. So your commands won’t run, and your log files won’t be written…

Instead you must define all your environment variables straight:

 SOME_DIR=/var/log MY_LOG_FILE=/var/log/some_file.log BIN_DIR=/usr/local/bin MY_EXE=/usr/local/bin/some_executable_file 0 10 * * * ${MY_EXE} some_param >> ${MY_LOG_FILE} 

When a task is run within cron, stdin is closed. Programs that act differently based on whether stdin is available or not will behave differently between the shell session and in cron.

An example is the program goaccess for analysing web server log files. This does NOT work in cron:

 goaccess -a -f /var/log/nginx/access.log > output.html 

and goaccess shows the help page instead of creating the report. In the shell this can be reproduced with

 goaccess -a -f /var/log/nginx/access.log > output.html < /dev/null 

The fix for goaccess is to make it read the log from stdin instead of reading from the file, so the solution is to change the crontab entry to

 cat /var/log/nginx/access.log | goaccess -a > output.html 

On my RHEL7 servers, root cron jobs would run, but user jobs would not. I found that without a home directory, the jobs won’t run (but you will see good errors in /var/log/cron). When I created the home directory, the problem was solved.

If you edited your crontab file using a windows editor (via samba or something) and it’s replaced the newlines with \n\r or just \r, cron won’t run.

Also, if you’re using /etc/cron.d/* and one of those files has a \r in it, cron will move through the files and stop when it hits a bad file. Not sure if that’s the problem?

使用:

 od -c /etc/cron.d/* | grep \r