如何找到脚本所在的目录?

670

请考虑以下Python代码:

import os
print os.getcwd()

我使用os.getcwd()获取脚本文件的目录位置。当我从命令行运行脚本时,它会给出正确的路径,但是当我从Django视图中的代码运行它时,它会打印/

如何在由Django视图运行的脚本中获取脚本的路径?

更新:
总结到目前为止的答案 - os.getcwd()os.path.abspath()都会给出当前工作目录,可能不是脚本所在的目录。在我的Web主机设置中,__file__只提供文件名而没有路径。

在Python中难道没有一种(始终)能够接收脚本所在路径的方法吗?


2
你应该更仔细地阅读那篇链接的文章。它从未建议使用getcwd来获取脚本的位置,而是建议使用argv[0]dirnameabspath - Rob Kennedy
@Rob - 在我的 Web 主机上,仅使用“print sys.argv [0]”将只提供文件名,而不包括路径。 - Jonathan Livni
@Rob - 这是来自链接文章的摘录:"os.getcwd() 返回当前工作目录。" - Jonathan Livni
9
是的,但当前工作目录与您的脚本所在目录没有任何关系。与os.chdir相比,该命令用于设置当前工作目录;它不会将您的脚本文件移动到硬盘驱动器上的新位置。初始工作目录可能与您的脚本所在目录相同,但并不总是这样;该文章甚至进行了演示。 - Rob Kennedy
2
请注意,__file__将返回脚本上下文的文件名。如果您从__main__调用外部脚本,则应该谨慎小心 - 您可能会得到与预期不同的响应。 - user559633
@jfs?重复的是哪篇文章?SO.UI允许您在将某些内容标记为重复时指定链接吗? - Jesse Chisholm
12个回答

1072

你需要对__file__调用os.path.realpath,这样当__file__是不带路径的文件名时,你仍然可以获取到目录路径:

import os
print(os.path.dirname(os.path.realpath(__file__)))

58
如果您正在解释器内运行,这将无法正常工作,因为会出现“NameError: name 'file' is not defined”的错误提示。 - Ehtesh Choudhury
51
我认为这是预期行为,因为该Python命令不在文件中,而是在您传递给解释器的字符串中。 - Har
12
这是因为__file__是一个模块变量,只有在脚本被执行时才会创建 -> 这个变量代表脚本的位置。解释器没有从文件运行,所以它不能有这样的变量。 - Zizouz212
5
迈克,哪里错了?os.path.dirname(os.path.realpath(__file__)) == sys.path[0] 这两个是相同的。 - bobpaul
5
这个解决方案提供了当前文件的目录。如果这是一个包中的模块(即脚本目录的子目录),则它与脚本目录不同。如果需要后者,请改用 sys.path[0]。要尝试它,请创建 t/foo.py 并打印两个值,然后创建仅包含 import t.foobar.py - Vedran Šego
显示剩余7条评论

208

尝试使用 sys.path[0]

引用自Python文档:

  

在程序启动时初始化,此列表的第一项 path[0] 是包含用于调用Python解释器的脚本的目录。如果脚本目录不可用(例如,如果交互地调用解释器或者从标准输入读取脚本),那么 path[0] 是一个空字符串,这将指导Python首先在当前目录中搜索模块。请注意,脚本目录被插入在由 PYTHONPATH 添加的条目之前。

来源: https://docs.python.org/library/sys.html#sys.path


19
通常情况下是这样的,但是这个方法返回的是当前运行脚本的文件路径,而不是被调用脚本的目录。换句话说,如果我从/foo/bar.py调用脚本/baz.py,那么这个解决方案将返回/foo,而非期望的/ - Edwin
1
我在实现这个方案时遇到了一个问题。我正在构建一个名为“foo”的大型程序,其中主要脚本名为“main.py”,并位于“foo”目录中。我可以通过运行“python foo/main.py”或仅运行“python foo”来调用我的程序。前者,您的解决方案有效,但后者会导致我得到相对路径而不是绝对路径。在您的解决方案周围包装“os.path.realpath()”即可解决此问题。 - Adam Stewart
sys.argv[0] 在我的一个案例中失败了:当我使用py2app在MacOS上将我的脚本封装成应用程序时,sys.argv[0]变成了硬编码的my.app/Contents/Resources/lib/python36.zip,而所有支持脚本都在my.app/Contents/Resources中。这可能是一个py2app的bug,但它证明依赖argv仍然有点脆弱。 - kakyo
我在我的bin目录中调用一个脚本作为符号链接,指向一个不在我的PATH中的可执行Python文件。然而,sys.path[0]显示的是我的bin目录,而不是正在运行的脚本的绝对路径。 - Brian Minton
sys.path[0] 指的是脚本运行的位置,大多数情况下与包含脚本的目录相同。如果将脚本编译为二进制文件并执行,则 sys.path[0] 将指向 %temp%,因为 exe 文件从 temp 目录运行。至少在 Windows 上是这样的。 - user6037143

158

我使用:

import os
import sys

def get_script_path():
    return os.path.dirname(os.path.realpath(sys.argv[0]))

正如aiham在评论中所指出的那样,您可以在模块中定义此函数并在不同的脚本中使用它。


16
+1 是因为有用的 __file__ 模块属性并非总是被定义。 - iacopo
5
如果您想将 getScriptPath() 放在不同的模块中,但仍需获取实际执行的文件路径而非模块路径,则此方法也很有用。 - aiham
@aiham:好观点。实际上,这个函数在我的框架工具模块中 :) - neuro
1
只是一个提示,这将返回执行的Python脚本的路径。如果您在其他地方将这些代码作为包,则这些代码将不会返回Python文件的源代码。 - notalentgeek
4
argv[0]不是可靠的查找脚本路径/文件名的方式。它可能是符号链接的名称,也可能是传递给execve(2)系统调用的任何其他内容。 - youfu
显示剩余3条评论

22

这段代码:

import os
dn = os.path.dirname(os.path.realpath(__file__))

将"dn"设置为包含当前执行脚本的目录的名称。这段代码:

fn = os.path.join(dn,"vcb.init")
fp = open(fn,"r")

将"fn"设置为"script_dir/vcb.init"(以一种跨平台的方式),并以当前正在执行的脚本为参数打开该文件。

请注意,“当前正在执行的脚本”有些模糊。如果您的整个程序只包含一个脚本,那么这就是当前正在执行的脚本,而“sys.path[0]”解决方案很好用。但是,如果您的应用程序由脚本A组成,该脚本导入某个包“P”,然后调用脚本“B”,那么“P.B”就是当前正在执行的脚本。如果您需要获取包含“P.B”的目录,则需要使用"os.path.realpath(__file__)"解决方案。

"__file__"仅提供当前执行(堆栈顶部)脚本的名称:“x.py”。它不提供任何路径信息。是"os.path.realpath"函数起到实际作用。


19
import os,sys
# Store current working directory
pwd = os.path.dirname(__file__)
# Append current directory to the python path
sys.path.append(pwd)

这在我的开发机上运行得很好,但在我的网络主机上不起作用 - 我得到'/'。 - Jonathan Livni
奇怪,你有访问Apache配置文件的权限吗?还是这是一个Windows服务器? - jbcurtin

12

使用os.path.abspath('')函数。


1
这在我的开发机器上适用于所有内容,但仅适用于Web主机上的脚本文件。它不适用于settings.py,例如以下内容无法工作:TEMPLATE_DIRS = ((os.path.abspath('')+'/templates'),) - Jonathan Livni
这对我没用。我通过符号链接从我的 PATH 调用了一个不在其中的可执行 Python 脚本,它只打印了我的 bin 目录。 - Brian Minton
1
哇...这个代码可以应对所有的困难情况,比如从父文件夹启动笔记本电脑或在REPL中运行。这应该是被接受的答案! - Shital Shah
1
这对我有用,而上面的答案没有,因为我正在运行嵌入在另一个程序中的Python。然而,我想知道它与os.getcwd()相比如何,因为它为我提供了相同的结果。 - creativecoding
1
这真是个天才想法,根据单元测试是通过PyCharm还是控制台启动来定义文件夹布局。 - Azzonith
这实际上与os.getcwd()是一样的,所以它并不完全等同于os.path.dirname(__file__) - kakyo

9

9
import os
script_dir = os.path.dirname(os.path.realpath(__file__)) + os.sep

4

这是我最终的结果。如果我将我的脚本导入解释器,或者将其作为脚本执行,这对我来说都有效:

import os
import sys

# Returns the directory the current script (or interpreter) is running in
def get_script_directory():
    path = os.path.realpath(sys.argv[0])
    if os.path.isdir(path):
        return path
    else:
        return os.path.dirname(path)

3

这是一个比较旧的帖子,但是当我尝试从cron作业中运行Python脚本时,尝试将文件保存到当前目录中时,我遇到了这个问题。getcwd() 和很多其他路径都显示为您的主目录。

如果要获取脚本的绝对路径,可以使用以下方法:

directory = os.path.abspath(os.path.dirname(__file__))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接