如何在Python中正确地确定当前的脚本目录?

我想看看什么是最好的方式来确定当前脚本目录在python中?

我发现这两种调用python代码的方法很多,很难find一个好的解决scheme。

这里有一些问题:

  • 如果脚本使用execexecfile执行,则__file__未定义
  • __module__仅在模块中定义

用例:

  • ./myfile.py
  • python myfile.py
  • ./somedir/myfile.py
  • python somedir/myfile.py
  • execfile('myfile.py') (来自另一个脚本,可以位于另一个目录中,可以有另一个当前目录。

我知道没有完美的解决scheme,因为在某些情况下,我正在寻找解决大部分案件的最佳方法。

最常用的方法是os.path.dirname(os.path.abspath(__file__))但是如果使用exec()从另一个脚本执行脚本,这种方法是os.path.dirname(os.path.abspath(__file__))

警告

任何使用当前目录的解决scheme都会失败,根据脚本的调用方式,这可能会有所不同,或者可以在运行脚本中更改。

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

确实是最好的你会得到。

exec / execfile执行一个脚本是很不寻常的; 通常您应该使用模块基础结构来加载脚本。 如果您必须使用这些方法,我build议在您传递给脚本的globals设置__file__ ,以便它可以读取该文件名。

没有其他办法可以在execed代码中获得文件名:正如你注意到的那样,CWD可能处在一个完全不同的地方。

如果您确实想要介绍通过execfile(...)调用脚本的情况,则可以使用inspect模块来推断文件名(包括path)。 据我所知,这将适用于您列出的所有情况:

 filename = inspect.getframeinfo(inspect.currentframe()).filename path = os.path.dirname(os.path.abspath(filename)) 
 #!/usr/bin/env python import inspect import os import sys def get_script_dir(follow_symlinks=True): if getattr(sys, 'frozen', False): # py2exe, PyInstaller, cx_Freeze path = os.path.abspath(sys.executable) else: path = inspect.getabsfile(get_script_dir) if follow_symlinks: path = os.path.realpath(path) return os.path.dirname(path) print(get_script_dir()) 

它适用于CPython,Jython,Pypy。 如果脚本使用execfile()sys.argv[0]和基于__file__的解决scheme在这里失败)执行脚本。 如果脚本在一个可执行的zip文件(/蛋)里面,它是有效的。 如果脚本是从zip文件“导入”的( PYTHONPATH=/path/to/library.zip python -mscript_to_run ) 它在这种情况下返回存档path。 如果脚本被编译为独立的可执行文件( sys.frozen ), sys.frozen 。 它适用于符号链接( realpath消除符号链接)。 它在一个交互式解释器中工作。 它在这种情况下返回当前的工作目录。

只需使用os.path.dirname(os.path.abspath(__file__))并仔细检查是否真的需要使用exec的情况。 如果您无法将脚本作为模块使用,则可能是devise出现问题的标志。

请记住Python#8的Zen ,如果您认为有必要为exec提供一个用例,那么请告诉我们关于这个问题背景的更多细节。

这应该在大多数情况下工作:

 import os,sys dirname=os.path.dirname(os.path.realpath(sys.argv[0])) 

首先..如果我们正在讨论注入匿名代码的方法,这里有一对夫妇丢失了用例。

 code.compile_command() code.interact() imp.load_compiled() imp.load_dynamic() imp.load_module() __builtin__.compile() loading C compiled shared objects? example: _socket?) 

但是,真正的问题是,你的目标是什么 – 你是否试图强制某种安全? 或者你只是在什么被加载感兴趣。

如果您对安全性感兴趣,那么通过exec / execfile导入的文件名是无关紧要的 – 您应该使用rexec ,它提供了以下内容:

该模块包含RExec类,它支持r_eval(),r_execfile(),r_exec()和r_import()方法,它们是标准Python函数eval(),execfile()和exec和import语句的受限版本。 在这个受限制的环境中执行的代码只能访问被视为安全的模块和函数。 您可以根据需要inheritanceRExec的添加或删除function。

然而,如果这更多的是一个学术的追求..这里有一些愚蠢的方法,你可能会深入挖掘一点。

示例脚本:

./deep.py

 print ' >> level 1' execfile('deeper.py') print ' << level 1' 

./deeper.py

 print '\t >> level 2' exec("import sys; sys.path.append('/tmp'); import deepest") print '\t << level 2' 

/tmp/deepest.py

 print '\t\t >> level 3' print '\t\t\t I can see the earths core.' print '\t\t << level 3' 

./codespy.py

 import sys, os def overseer(frame, event, arg): print "loaded(%s)" % os.path.abspath(frame.f_code.co_filename) sys.settrace(overseer) execfile("deep.py") sys.exit(0) 

产量

 loaded(/Users/synthesizerpatel/deep.py) >> level 1 loaded(/Users/synthesizerpatel/deeper.py) >> level 2 loaded(/Users/synthesizerpatel/<string>) loaded(/tmp/deepest.py) >> level 3 I can see the earths core. << level 3 << level 2 << level 1 

当然,这是一个资源密集的方式来做到这一点,你会跟踪所有的代码..不是很有效率。 但是,我认为这是一种新颖的方法,因为即使你深入巢穴,它也能继续工作。 你不能重写'eval'。 虽然你可以覆盖execfile()。

请注意,这种方法只包含exec / execfile,而不是“导入”。 对于更高级别的“模块”负载挂钩,您可以使用sys.path_hooks (PyMOTW提供)。

这就是我所有的头脑。

在Python 3.4+中,您可以使用更简单的pathlib模块:

 from inspect import currentframe, getframeinfo from pathlib import Path filename = getframeinfo(currentframe()).filename parent = Path(filename).resolve().parent 

这是一个部分的解决scheme,比迄今为止发布的所有方法都要好。

 import sys, os, os.path, inspect #os.chdir("..") if '__file__' not in locals(): __file__ = inspect.getframeinfo(inspect.currentframe())[0] print os.path.dirname(os.path.abspath(__file__)) 

现在这个工作将会全部调用,但是如果有人使用chdir()来改变当前目录,这也将失败。

笔记:

希望这有助于: – 如果从任何地方运行脚本/模块,您将可以访问代表脚本位置的模块variables__file__variables。

另一方面,如果使用解释器,则无法访问该variables,如果您从名称NameError获取名称,并且如果正在运行文件,则os.getcwd()会为您提供不正确的目录别的地方。

这个解决scheme应该给你在所有情况下你要找的东西:

 from inspect import getsourcefile from os.path import abspath abspath(getsourcefile(lambda:0)) 

我没有彻底testing它,但它解决了我的问题。

 import os cwd = os.getcwd() 

做你想做的? 我不确定你的意思是“当前脚本目录”。 你给的用例的预期输出是多less?