题 手动立即运行cron作业


(我已经读过了 如何测试新的cron脚本?。)

我有一个特定的问题(cron作业似乎没有运行,或运行正常),但问题是一般的:我想调试cronned脚本。我知道我可以设置一个* * * * * crontab行,但这不是一个完全令人满意的解决方案。我希望能够从命令行运行一个cron作业,好像cron正在运行它(相同的用户,相同的环境变量等)。有没有办法做到这一点?必须等待60秒才能测试脚本更改是不切实际的。


94
2017-11-18 13:55




(抱歉无法添加评论)0 30 16 20 *? *即使你运行这样的工作,整个想法是提供脚本输出,看看出现什么问题,除非工作写入日志,这是无用的


答案:


这就是我所做的,它似乎在这种情况下起作用。至少,它显示一个错误,而从命令行运行,因为用户没有显示错误。


步骤1:我暂时将此行放在用户的crontab中:

* * * * *   /usr/bin/env > /home/username/tmp/cron-env

然后在文件写完后将其取出。

第2步:让自己成为一个run-as-cron bash脚本,其中包含:

#!/bin/bash
/usr/bin/env -i $(cat /home/username/tmp/cron-env) "$@"

那么,作为有问题的用户,我能够

run-as-cron /the/problematic/script --with arguments --and parameters

显然,这个解决方案可以扩展到使用sudo等,以获得更大的灵活性。

希望这有助于其他人。


73
2017-11-18 14:40



这对我不起作用,我想知道是否适用于任何投票的人。 1)你为什么使用bash?这里不需要它,它可能不在 /usr/bin。 2) cat …/cron-env 输出多行,这是行不通的。只是尝试执行 /usr/bin/env -i $(cat cron-env) echo $PATH 在终端中,它按字面输出环境而不是使用它。 3)当前环境泄漏到模拟的cron环境中。尝试: export foo=leaked; run-as-cron echo $foo。 - Marco
@Marco在bash中工作,这是我使用的,因为它是比sh更好的定义环境。我使用了pdksh,ksh(多个版本),bash和dash中的所有内容,因此我非常清楚sh的“纯”实现之间的差异,即使非常严格地保留在语言的公共子集中也是如此。 :-) - Max Murphy
@Marco 2。 cat 输出多行,这是有效的,因为shell替换将它们折叠成一行,您可以检查它 echo $(cat cron-env ) | wc;你的示例命令, /usr/bin/env -i $(cat cron-env) echo $PATH,替代品 $PATH 来自调用shell;相反,它应该调用一个子shell来代替subenvironement,例如 /usr/bin/env -i $(cat cron-env) /bin/sh -c 'echo $PATH'。 3.你犯了同样的错误,再次在调用shell中替换,而不是在子环境中 - John Freeman


我提出了一个基于Pistos答案的解决方案,但没有缺陷。

  • 将以下行添加到crontab,例如运用 crontab -e

    * * * * *  /usr/bin/env > /home/username/cron-env
    
  • 创建一个shell脚本,该脚本在与cron作业运行相同的环境中执行命令:

    #!/bin/sh
    
    . "$1"
    exec /usr/bin/env -i "$SHELL" -c ". $1; $2"
    

使用:

run-as-cron <cron-environment> <command>

例如

run-as-cron /home/username/cron-env 'echo $PATH'

请注意,如果需要参数,则需要引用第二个参数。 该脚本的第一行加载POSIX shell作为解释器。第二行来源cron环境文件。这是加载正确的shell所必需的,该shell存储在环境变量中 SHELL。然后它加载一个空的环境(以防止环境变量泄漏到新的shell中),启动用于cronjobs的相同shell并加载cron环境变量。最后执行该命令。


34
2017-09-24 18:46



这有助于我重现我与红宝石相关的狮身人面像加载错误。 - cweiske
我使用@reboot cron选项编写cron-env文件。然后,您可以将其保留在crontab中,并且只有在系统启动时才会重写它。它使它更简单,因为您不必添加/删除行。 - Michael Barton


由于crontab不能完成这项工作,你将操纵它的内容:

crontab -l | grep -v '^#' | cut -f 6- -d ' ' | while read CMD; do eval $CMD; done

它能做什么 :

  • 列出crontab作业
  • 删除评论行
  • 删除crontab配置
  • 然后一个接一个地启动它们

14
2017-08-19 19:45



但这并不一定在cron会在同一环境中进行,我认为他只想测试其中一个。 - Falcon Momot
对,我错了......它只能运行工作但不像cron那样做! - Django Janny
仍然是一个很棒的解决方案+1 - Eric Uldall


默认情况下,我见过的大多数默认cron守护进程,根本就没有办法告诉cron现在就在这里运行。如果您正在使用anacron,我可能会认为在前台运行单独的实例。

如果您的脚本运行不正常,那么您没有考虑到这一点

  • 该脚本作为特定用户运行
  • cron有一个受限制的环境(最明显的表现是不同的路径)。

来自crontab(5):

设置了几个环境变量   由cron自动启动(8)   守护进程。 SHELL设置为/ bin / sh,和   LOGNAME和HOME是从中设置的   crontab的/ etc / passwd行   所有者。 PATH设置为“/ usr / bin:/ bin”。   HOME,SHELL和PATH可能是   被设置覆盖了   crontab的; LOGNAME就是那个用户   工作正在运行,也可能不是   改变。

一般来说,PATH是最大的问题,所以你需要:

  • 在测试时,将脚本中的PATH显式设置为/ usr / bin:/ bin。你可以用bash做到这一点 export PATH =“/ usr / bin:/ bin”
  • 在crontab的顶部显式设置所需的正确PATH。例如PATH = “在/ usr /斌:/ bin中:在/ usr / local / bin目录:/ usr / sbin目录:/ sbin目录”

如果您需要以没有shell的其他用户身份运行脚本(例如www-data),请使用sudo:

sudo -u www-data /path/to/crontab-script.sh

当然,在所有这些之前测试的第一件事是你的脚本实际上从命令行做了它应该做的事情。如果你不能从命令行运行它,它显然不适用于cron。


5
2017-11-18 14:27



谢谢你的全面回复。我知道作为特定用户和特定环境运行的两个问题。因此,我已经制定了自己的答案,我现在将发布...... - Pistos
转义字符是作业未运行的正当理由 - Joe Phillips


好吧,用户与你放在crontab条目中的用户相同(或者你把它放入的crontab,交替),这是一个明智的选择。 crontab(5)应该给你设置的环境变量列表,只有几个。


1
2017-11-18 14:04



换句话说,你说没有办法做到这一点?只有“足够接近”的解决方法? - Pistos
不,我说你可以使用我在答案中提供的信息来做到这一点。 - womble♦


在大多数crontabs中,例如vixie-cron你可以像这样在crontab本身放置变量然后使用/ usr / bin / env检查它是否有效。通过这种方式,一旦发现run-as-cron脚本出错,就可以使脚本在crontab中运行。

SHELL=/bin/bash
LANG=en
FASEL=BLA

* * * * *   /usr/bin/env > /home/username/cron-env

1
2017-10-27 06:52





由于某种原因,马可的剧本对我不起作用。我没有时间调试,所以我编写了一个Python脚本,它做了同样的事情。它更长,但是:首先,它适用于我,其次,我发现它更容易理解。将“/ tmp / cron-env”更改为保存环境的位置。这里是:

#!/usr/bin/env python
from __future__ import division, print_function

import sys
import os

def main():
    if len(sys.argv) != 2 or sys.argv[1] in ('-h', '--help'):
        print("Usage: {} CMD\n"
              "Run a command as cron would. Note that CMD must be quoted to be only one argument."
              .format(sys.argv[0]))
        sys.exit(1)
    _me, cmd = sys.argv
    env = dict(line.strip().split('=', 1) for line in open('/tmp/cron-env'))
    sh = env['SHELL']
    os.execvpe(sh, [sh, '-c', cmd], env)

if __name__ == '__main__':
    main()

1
2017-12-24 08:48





Marco的解决方案对我不起作用,但Noam的python脚本有效。以下是对Marco的脚本进行的略微修改,使其适用于我:

#!/bin/sh
. "$1"
exec /usr/bin/env -i "$SHELL" -c "set -a;. $1; $2"

补充说 set -a 导出变量在脚本$ 1中定义,并使其可用于命令$ 2

附: Noam的python之所以有效,是因为它将'环境'输出到了子进程。


1
2018-05-11 05:05





我从来没有找到过手动运行cron作业的方法 这个 记录建议设置与cronjob相同的环境并手动运行脚本。


0
2017-11-18 14:07



是不是你建议做什么OP想知道怎么做? - womble♦
这就是为什么我将链接包含在描述如何操作的文章中。我认为没有必要在这里复制粘贴一切。 - oneodd1


你可以编程工作开始下一分钟:)


0
2017-09-23 13:52



59秒是很多时间。 - Stéphane Bruckert
OP在问题中提到了这种可能性:“有没有办法做到这一点?等待60秒来测试脚本更改是不切实际的。” - Andrew Grimm
59秒可能比选择和实施任何其他提议(并且不保证可行)解决方案所需的时间少。当我看到这些缺点时,我想知道Linux如何成为这样一个事实上的标准服务器操作系统。难道没有任何严肃的系统管理员想要测试他们的工作吗? - Rolf