我正在尝试从cron运行Django管理命令。我使用virtualenv保持我的项目沙盒。
我在这里和其他地方看到了从virtualenv中运行管理命令的示例,例如:
0 3 * * * source /home/user/project/env/bin/activate && /home/user/project/manage.py command arg
然而,尽管syslog显示了任务应该在何时启动的条目,但该任务从未实际运行(脚本的日志文件为空)。如果我从shell中手动运行这一行,它将按预期工作。
我目前可以通过cron运行命令的唯一方法是将命令分解并将它们放在一个哑bash包装脚本中:
#!/bin/sh
source /home/user/project/env/bin/activate
cd /home/user/project/
./manage.py command arg
编辑:
Ars提出了一个命令的工作组合:
0 3 * * * cd /home/user/project && /home/user/project/env/bin/python /home/user/project/manage.py command arg
至少在我的例子中,调用virtualenv的激活脚本没有任何作用。这招管用,所以节目继续。
这是一种保持crontab命令与常规命令非常相似的简单方法(在Ubuntu 18.04中进行了测试)。一些需要记住的关键注意事项:
你可以使用。命令,而不是源。(crontab默认使用sh,而不是bash,因此它没有source。)
~和$变量在crontab命令中展开。(只有crontab环境语句不进行变量展开。)
这里有一些例子,如果你有一个文件~/myproject/main.py:
* * * * * cd ~/myproject && . .venv/bin/activate && python main.py > /tmp/out1 2>&1
您也可以直接调用venv目录中python的特定路径,然后不需要调用activate。
* * * * * ~/myproject/.venv/bin/python ~/myproject/main.py > /tmp/out2 2>&1
这样做的缺点是您需要指定两次项目路径,这使得维护更加棘手。为了避免这种情况,你可以使用一个shell变量,这样你只指定一次项目路径:
* * * * * project_dir=~/myproject ; $project_dir/.venv/bin/python $project_dir/main.py > /tmp/out3 2>&1
由于cron在它自己的最小sh环境中执行,下面是我在虚拟环境中运行Python脚本的方法:
* * * * * . ~/.bash_profile; . ~/path/to/venv/bin/activate; python ~/path/to/script.py
(注:如果。~ /。Bash_profile不适合您,那么请尝试一下。~ /。Bashrc或。~ /。配置文件取决于您的服务器如何设置。)
这将加载您的bash shell环境,然后激活您的Python虚拟环境,基本上为您留下与测试脚本相同的设置。
不需要在crontab中定义环境变量,也不需要修改现有脚本。
对我来说,最好的解决办法是两者兼得
使用venv bin/目录下的python二进制文件
设置python路径
要包含venv模块目录。
man python提到在shell中修改路径$PYTHONPATH或在python中修改sys.path
其他回答提到了使用shell来实现这一点的想法。从python中,向我的脚本中添加以下行可以让我成功地直接从cron运行它。
import sys
sys.path.insert(0,'/path/to/venv/lib/python3.3/site-packages');
这是它在互动会话中的样子
Python 3.3.2+ (default, Feb 28 2014, 00:52:16)
[GCC 4.8.1] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> sys.path
['', '/usr/lib/python3.3', '/usr/lib/python3.3/plat-x86_64-linux-gnu', '/usr/lib/python3.3/lib-dynload']
>>> import requests
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: No module named 'requests'
>>> sys.path.insert(0,'/path/to/venv/modules/');
>>> import requests
>>>