如何可靠地打开与当前正在运行的脚本位于同一目录中的文件
Posted
技术标签:
【中文标题】如何可靠地打开与当前正在运行的脚本位于同一目录中的文件【英文标题】:How to reliably open a file in the same directory as the currently running script 【发布时间】:2011-05-02 21:25:35 【问题描述】:我曾经通过简单地使用如下命令打开与当前运行的 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__)
返回用于调用脚本的路径,可能是相对的,甚至是空白的(如果脚本在 cwd 中)。此外,当脚本在 IDLE 或 PythonWin 中运行时,__file__
不存在。
sys.path[0]
和 os.path.abspath(os.path.dirname(sys.argv[0]))
似乎返回了脚本目录。我不确定这两者之间是否有任何区别。
编辑:
我刚刚意识到我想要做的最好描述为“在与包含模块相同的目录中打开一个文件”。换句话说,如果我导入了我在另一个目录中编写的模块,并且该模块打开了一个文件,我希望它在模块的目录中查找该文件。我认为我发现的任何东西都无法做到这一点......
【问题讨论】:
“错误的目录”是不正确的分析。双击或否,脚本运行在你的当前工作目录,而不是你保存脚本的目录。 【参考方案1】:我总是用:
__location__ = os.path.realpath(
os.path.join(os.getcwd(), os.path.dirname(__file__)))
join()
调用会在当前工作目录之前添加,但文档中说如果某个路径是绝对路径,则它剩下的所有其他路径都将被删除。因此,getcwd()
在dirname(__file__)
返回绝对路径时被删除。
此外,realpath
调用会解析符号链接(如果找到)。这避免了在 Linux 系统上使用 setuptools 部署时的麻烦(脚本符号链接到 /usr/bin/
-- 至少在 Debian 上)。
您可以使用以下方法打开同一文件夹中的文件:
f = open(os.path.join(__location__, 'bundled-resource.jpg'))
# ...
我用它来将资源与 Windows 和 Linux 上的几个 Django 应用程序捆绑在一起,它就像一个魅力!
【讨论】:
如果无法使用__file__
,则使用sys.argv[0]
而不是dirname(__file__)
。其余的应该按预期工作。我喜欢使用__file__
,因为在库代码中,sys.argv[0]
可能根本不指向您的代码,尤其是通过某些第 3 方脚本导入时。
这个问题是,如果您正在运行的文件是直接来自中断器或如果它是导入的,它会有所不同。请参阅我对 file 和 sys.argv[0] 之间差异的回答
那么说 Zimm3r 的答案中描述的变化是通过使用realpath( join( getcwd(), dirname(__file__) ))
来解决的,如此处所述是否正确?
没有必要使用getcwd()
,因为os.path.abspath()
函数为您完成了这项工作。 os.path.realpath()
打电话给os.path.abspath()
。
更现代的方法是通过open(pathlib.Path(__file__).parent / 'Some file.txt')
【参考方案2】:
我会这样做:
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 构建文件的绝对路径,等效于使用normpath(join(os.getcwd(), path))
[来自pydocs]。然后它会检查该文件是否实际上是exists,然后使用上下文管理器打开它,这样您就不必记住在文件句柄上调用close。恕我直言,从长远来看,这样做会为您节省很多痛苦。
【讨论】:
这没有回答发帖人的问题。 dln385 专门说os.path.abspath
如果脚本不在当前目录,则不会解析到与脚本在同一文件夹中的文件的路径。
啊!我假设用户正在与他们想要读取的文件相同的目录中运行此脚本,NOT 在他们 PYTHONPATH 中某些内容的模块目录中。这会教我做出假设......
abspath 将不起作用,因为 python 运行时不可能使用这样的函数搜索 OS 文件系统。【参考方案3】:
好吧,这就是我的工作
sys.argv 始终是您在终端中键入的内容,或者在使用 python.exe 或 pythonw.exe 执行时用作文件路径
例如,您可以通过多种方式运行文件 text.py,它们每一种都给您不同的答案,它们总是给您输入 python 的路径。
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\
无论你输入 python test.py 还是 python "C:\Documents and Settings\Admin\test.py",它都会输出这个
使用__file__的问题 考虑这两个文件 测试.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
如您所见,file 始终为您提供运行它的 python 文件,而 sys.argv[0] 始终为您提供从解释器运行的文件。根据您的需求,您需要选择最适合您需求的一种。
【讨论】:
这是一个详细的证明,证明实现反映了文档。__file__
supposed 为“始终为您提供当前文件的路径”,sys.argv[0]
supposed 为“始终提供启动过程”。在任何情况下,在被调用的脚本中使用 __file__
总是会给你精确的结果。
如果您在脚本的顶层引用了__file__
,它将按预期工作。
sys.argv[0]
设置为父进程告诉操作系统将其设置为的任何值。使用#!/usr/env python
作为test.py
的第一行,使文件可执行,然后使用alias foo test.py
。或创建文件的符号链接。不管怎样,现在sys.argv[0]
将是错误的。或者使用其中一个os.exec*()
functions 运行脚本并为第一个参数选择您自己的值。不要依赖sys.argv
告诉你脚本的名称!确定脚本的目录时,使用__file__
。【参考方案4】:
引用 Python 文档:
在程序启动时初始化,此列表的第一项路径 [0] 是包含用于调用 Python 解释器的脚本的目录。如果脚本目录不可用(例如,如果以交互方式调用解释器或从标准输入读取脚本),则 path[0] 是空字符串,它指示 Python 首先搜索当前目录中的模块。请注意,脚本目录是在作为 PYTHONPATH 插入的条目之前插入的。
如果您从终端运行脚本,sys.path[0]
就是您要查找的内容。
但是,如果您有:
barpath/bar.py
import foopath.foo
foopath/foo.py
print sys.path[0] # you get barpath
所以要小心!
【讨论】:
对于文件的完整路径:os.path.join(sys.path[0], 'some file.txt')
。这应该在所有系统上正确处理空格和斜杠。
这是第一个问题的答案,而不是 EDIT 之后的答案。
sys.argv[0]
设置为父进程告诉操作系统将其设置为的任何值。使用#!/usr/env python
作为名为@987654327@ 的脚本的第一行,使文件可执行,然后使用alias foo test.py
。或创建文件的符号链接。不管怎样,现在sys.argv[0]
将是错误的。或者使用os.exec*()
functions 之一运行脚本并为第一个参数选择您自己的值。不要依赖sys.argv
告诉你脚本的名称!确定脚本的目录时,使用__file__
。【参考方案5】:
在 Python 3.4 上,添加了 pathlib
module,以下代码将可靠地打开与当前脚本位于同一目录中的文件:
from pathlib import Path
p = Path(__file__).with_name('file.txt')
with p.open('r') as f:
print(f.read())
如果需要,您还可以使用parent.absolute()
将目录值作为字符串获取,以便使用某种open
-like API:
p = Path(__file__)
dir_abs = p.parent.absolute() # Will return the executable directory absolute path
【讨论】:
【参考方案6】:在尝试了所有这些解决方案之后,我仍然遇到了不同的问题。所以我发现最简单的方法是创建一个python文件:config.py,其中包含一个包含文件绝对路径的字典并将其导入脚本。 像
import config as cfg
import pandas as pd
pd.read_csv(cfg.paths['myfilepath'])
config.py 里面的位置:
paths = 'myfilepath': 'home/docs/...'
它不是自动的,但是当您必须在不同的目录或不同的机器上工作时,它是一个很好的解决方案。
【讨论】:
问题是明确要求Python脚本的目录,而不是硬编码的值,不管你如何“导入”【参考方案7】:我通常使用以下内容。它也适用于测试以及可能的其他用例。
with open(os.path.join(os.path.dirname(__file__), 'some_file.txt'), 'r') as f:
这个答案推荐在https://***.com/questions/10174211/how-to-make-an-always-relative-to-current-module-file-path
【讨论】:
以上是关于如何可靠地打开与当前正在运行的脚本位于同一目录中的文件的主要内容,如果未能解决你的问题,请参考以下文章
如何在 tkinter GUI 位于同一文件中的情况下运行 python 脚本?您如何处理用户条目?