python:将脚本工作目录更改为脚本自己的目录


171

我每分钟从crontab运行python shell:

* * * * * /home/udi/foo/bar.py

/home/udi/foo有一些必要的子目录,例如/home/udi/foo/log/home/udi/foo/config,它/home/udi/foo/bar.py指的是。

问题是crontab从另一个工作目录运行脚本,因此尝试打开./log/bar.log失败。

有没有办法告诉脚本将工作目录更改为脚本自己的目录?我想找到一种适用于任何脚本位置的解决方案,而不是明确告诉脚本位置。

编辑:

os.chdir(os.path.dirname(sys.argv[0]))

是最紧凑的优雅解决方案。感谢您的回答和解释!


无关crontab用例:既sys.argv[0]__file__,如果脚本使用运行失败execfile(); inspect基于解决方案可以代替使用。
jfs 2014年

Answers:


206

这会将当前工作目录更改为,以便打开相对路径将起作用:

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)

这将获取脚本的文件名,将其转换为绝对路径,然后提取该路径的目录,然后更改为该目录。


3
等于对目录进行硬编码。
Ikke

2
如果从符号链接运行它,则将无法正常工作。使用__file__代替sys.argv[0]
克里斯·唐

1
为什么要无礼的一步?为什么不简单os.chdir(os.path.dirname(__file__))
Panic Panic

8
__file__在“冻结”程序(使用py2exe,PyInstaller,cx_Freeze创建)中失败。sys.argv[0]作品。@ChrisDown:如果您想遵循符号链接;os.path.realpath()可用于。
jfs 2014年

3
@EliCourtwright如果__file__还不是绝对路径,并且用户已更改工作目录,则os.path.abspath无论如何都会失败。
亚瑟塔卡


23

不要这样

您的脚本和数据不应混入一个大目录中。把你的代码在一些已知的位置(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 )

不要根据软件的位置“假定”目录。从长远来看,它将无法很好地工作。


9
我认为您将大型软件包的代码和数据分离是正确的,但是对于小型维护脚本而言,这似乎是牵强的。我完全同意版本控制。
亚当·马坦

3
S. Lott是对的。除非数据不是暂时的,否则始终将数据和代码分开。例如,如果您有图标,那是数据,但不是暂时的,并且相对于软件包(无论意味着什么)考虑它是有意义的
Stefano

5
@Udi Pasmon:一点都不牵强。是使组织陷入严重麻烦的“小维护脚本”。从现在开始,多年以来,这种“小型维护脚本”及其子级,派生类和数据文件将成为解开和重新实现的噩梦。使数据尽可能远离代码-为所有内容传递参数-不做任何假设。
S.Lott

1
+1我以为我想做OP,但是在阅读了您的建议后,我修改了我的脚本。现在,它需要一个参数来指定日志文件的位置。
伊恩·塞缪尔·麦克莱恩

+1。如果可以轻松自定义数据目录,则为python脚本创建包(rpm)会更容易。
jfs 2014年

18

将您的crontab命令更改为

* * * * * (cd /home/udi/foo/ || exit 1; ./bar.py)

(...)您的crond执行作为一个单一的命令启动一个子shell。如果|| exit 1目录不可用,则导致您的cronjob失败。

尽管从长远来看,其他解决方案对于您的特定脚本可能更优雅,但是在您无法修改要执行的程序或命令的情况下,我的示例仍然有用。


1
这是一个非常合理的解决方案。我通常会发现自己在编辑其他人的答案以添加|| exit 1。看到这令人耳目一新。尽管我确实想知道您为什么不这样做cd /home/udi/foo/ && ./bar.py
Bruno Bronosky

2
@BrunoBronosky使用明确的名称,exit 1您的crond将收到错误通知,并且在大多数情况下,将通过电子邮件发送失败通知。
Ruud Althuizen
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.