如何在Python脚本的相同目录下可靠地打开一个文件
我曾经使用类似命令打开与当前运行的Python脚本位于同一目录中的文件
open("Some file.txt", "r")
不过,我发现当脚本在Windows下双击运行时,会尝试从错误的目录中打开文件。
从那以后,我用了一个表单命令
open(os.path.join(sys.path[0], "Some file.txt"), "r")
每当我想打开一个文件。 这适用于我的特定用法,但我不确定sys.path[0]
是否可能在某些其他用例中失败。
所以我的问题是:什么是最好和最可靠的方式来打开一个文件,在目前正在运行的Python脚本在同一目录?
这是我迄今为止能够弄清楚的:
-
os.getcwd()
和os.path.abspath('')
返回“当前工作目录”,而不是脚本目录。 -
os.path.dirname(sys.argv[0])
和os.path.dirname(__file__)
返回用于调用脚本的path,可能是相对的,甚至是空白的(如果脚本在cwd中)。 另外,当脚本在IDLE或PythonWin中运行时,__file__不存在。 -
sys.path[0]
和os.path.abspath(os.path.dirname(sys.argv[0]))
似乎返回脚本目录。 我不确定这两者之间是否有区别。
编辑:
我只是意识到,我想要做的将被更好地描述为“在与包含模块相同的目录中打开文件”。 换句话说,如果我导入一个模块,我写在另一个目录中,那个模块打开一个文件,我希望它在模块的目录中查找文件。 我不认为我find的任何东西都能做到
我总是使用:
__location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__)))
join()
调用预先指定当前的工作目录,但是文档中说如果某个path是绝对path,则所有其他path都将被删除。 因此,当dirname(__file__)
返回绝对path时, getcwd()
被删除。
此外, realpath
调用会parsing符号链接(如果有的话)。 这可以避免在Linux系统上使用setuptools进行部署时的麻烦(脚本与/usr/bin/
至less在Debian上是相符的)。
您可以使用以下方式在相同的文件夹中打开文件:
f = open(os.path.join(__location__, 'bundled-resource.jpg')); # ...
我使用它在Windows和Linux上捆绑多个Django应用程序的资源,它的作用就像一个魅力!
引用Python文档:
在程序启动时初始化,这个列表中的第一个项目path [0]是包含用于调用Python解释器的脚本的目录。 如果脚本目录不可用(例如,如果解释器是交互式调用的,或者脚本是从标准input中读取的),则path [0]是空string,它指示Python首先search当前目录中的模块。 请注意,由于PYTHONPATH而插入的条目之前插入了脚本目录。
sys.path [0]是你在找什么。
好的,这是我做的
sys.argv总是input到terminal中,或者在使用python.exe或pythonw.exe执行时用作文件path
例如,你可以用几种方式运行文件text.py,它们每个都给你一个不同的答案,他们总是给你pythoninput的path。
C:\Documents and Settings\Admin>python test.py sys.argv[0]: test.py C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py" sys.argv[0]: C:\Documents and Settings\Admin\test.py
好,那么知道你可以得到文件名,非常重要,现在可以使用os.path来获取应用程序目录,具体来说就是abspath和dirname
import sys, os print os.path.dirname(os.path.abspath(sys.argv[0]))
这将输出这个:
C:\Documents and Settings\Admin\
它会一直输出这个,不pipe你inputpython test.py还是python“C:\ Documents and Settings \ Admin \ test.py”
使用__file__的问题考虑这两个文件test.py
import sys import os def paths(): print "__file__: %s" % __file__ print "sys.argv: %s" % sys.argv[0] a_f = os.path.abspath(__file__) a_s = os.path.abspath(sys.argv[0]) print "abs __file__: %s" % a_f print "abs sys.argv: %s" % a_s if __name__ == "__main__": paths()
import_test.py
import test import sys test.paths() print "--------" print __file__ print sys.argv[0]
“python test.py”的输出
C:\Documents and Settings\Admin>python test.py __file__: test.py sys.argv: test.py abs __file__: C:\Documents and Settings\Admin\test.py abs sys.argv: C:\Documents and Settings\Admin\test.py
“python test_import.py”的输出
C:\Documents and Settings\Admin>python test_import.py __file__: C:\Documents and Settings\Admin\test.pyc sys.argv: test_import.py abs __file__: C:\Documents and Settings\Admin\test.pyc abs sys.argv: C:\Documents and Settings\Admin\test_import.py -------- test_import.py test_import.py
所以你可以看到文件给你总是从它正在运行的python文件,在那里作为sys.argv [0]给你你从解释器总是运行的文件。 根据您的需求,您需要select最适合您的需求的产品。
我会这样做:
from os.path import abspath, exists f_path = abspath("fooabar.txt") if exists(f_path): with open(f_path) as f: print f.read()
上面的代码使用abspath构build了文件的绝对path,相当于使用normpath(join(os.getcwd(), path))
[来自pydocs]。 然后检查文件是否真的存在 ,然后使用上下文pipe理器打开它,所以你不必记得在文件句柄上调用close。 恕我直言,这样做会为您节省很多痛苦,从长远来看。