我每分钟从 crontab 运行一个 python shell:
* * * * * /home/udi/foo/bar.py
/home/udi/foo
有一些必要的子目录,例如 /home/udi/foo/bar.py
所指的 /home/udi/foo/log
和 /home/udi/foo/config
。
问题是 crontab
从不同的工作目录运行脚本,因此尝试打开 ./log/bar.log
失败。
有没有一种好方法可以告诉脚本将工作目录更改为脚本自己的目录?我想要一个适用于任何脚本位置的解决方案,而不是明确告诉脚本它在哪里。
编辑:
os.chdir(os.path.dirname(sys.argv[0]))
是最紧凑优雅的解决方案。感谢您的回答和解释!
这会将您当前的工作目录更改为以便打开相对路径将起作用:
import os
os.chdir("/home/udi/foo")
但是,您询问了如何更改 Python 脚本所在的目录,即使您在编写脚本时不知道该目录是什么目录。为此,您可以使用 os.path
函数:
import os
abspath = os.path.abspath(__file__)
dname = os.path.dirname(abspath)
os.chdir(dname)
这将获取脚本的文件名,将其转换为绝对路径,然后提取该路径的目录,然后更改为该目录。
您可以使用 sys.path[0]
获得更短的版本。
os.chdir(sys.path[0])
来自http://docs.python.org/library/sys.html#sys.path
在程序启动时初始化,此列表的第一项路径 [0] 是包含用于调用 Python 解释器的脚本的目录
import git
,这将不起作用。第一个路径将指向 <pythondir>/lib/site-packages/git/ext/gitdb。至少这发生在我身上
不要这样做。
你的脚本和你的数据不应该混合到一个大目录中。将您的代码放在与您的数据分开的某个已知位置(site-packages
或 /var/opt/udi
或其他位置)。对代码使用良好的版本控制,以确保当前和以前的版本相互分离,以便您可以回退到以前的版本并测试未来的版本。
底线:不要混合代码和数据。
数据很宝贵。代码来来去去。
提供工作目录作为命令行参数值。您可以提供默认值作为环境变量。不要推断(或猜测)
使其成为必需的参数值并执行此操作。
import sys
import os
working= os.environ.get("WORKING_DIRECTORY","/some/default")
if len(sys.argv) > 1: working = sys.argv[1]
os.chdir( working )
不要根据软件的位置“假设”一个目录。从长远来看,它不会很好地工作。
将您的 crontab 命令更改为
* * * * * (cd /home/udi/foo/ || exit 1; ./bar.py)
(...)
启动您的 crond 作为单个命令执行的子 shell。如果目录不可用,|| exit 1
会导致您的 cronjob 失败。
尽管从长远来看,对于您的特定脚本,其他解决方案可能更优雅,但在您无法修改要执行的程序或命令的情况下,我的示例仍然很有用。
|| exit 1
之类的内容。看到这个让人耳目一新。虽然我确实想知道为什么你不只是做cd /home/udi/foo/ && ./bar.py
exit 1
您的 crond 将收到错误通知,并且在大多数情况下会发送失败的电子邮件通知。
cd /home/udi/foo/ && ./bar.py
也会这样做。
__file__
而不是sys.argv[0]
。os.chdir(os.path.dirname(__file__))
?__file__
在“冻结”程序(使用 py2exe、PyInstaller、cx_Freeze 创建)中失败。sys.argv[0]
有效。 @ChrisDown:如果您想关注符号链接;os.path.realpath()
可以使用。__file__
还不是绝对路径,并且用户更改了工作目录,那么os.path.abspath
无论如何都会失败。