问题:如何正确确定当前脚本目录?
我想看看确定python中当前脚本目录的最佳方法是什么?
我发现,由于调用python代码的方式很多,很难找到一个好的解决方案。
这里有一些问题:
__file__
如果脚本与执行没有定义exec
,execfile
__module__
仅在模块中定义
用例:
./myfile.py
python myfile.py
./somedir/myfile.py
python somedir/myfile.py
execfile('myfile.py')
(来自另一个脚本,该脚本可以位于另一个目录中,并且可以具有另一个当前目录。
我知道没有完美的解决方案,但是我正在寻找能够解决大多数情况的最佳方法。
最常用的方法是,os.path.dirname(os.path.abspath(__file__))
但是如果您使用来从另一个脚本执行脚本,则此方法实际上不起作用exec()
。
警告
使用当前目录的任何解决方案都会失败,这可能会因调用脚本的方式而有所不同,或者可以在运行的脚本中进行更改。
回答 0
os.path.dirname(os.path.abspath(__file__))
确实是您将获得的最好的。
用exec
/ 执行脚本是不寻常的execfile
。通常,您应该使用模块基础结构来加载脚本。如果必须使用这些方法,我建议设置__file__
在globals
传递给脚本,以便它可以读取该文件名。
没有其他方法可以在执行代码中获取文件名:如您所述,CWD可能位于完全不同的位置。
回答 1
如果您确实想解决通过调用脚本的情况,则execfile(...)
可以使用该inspect
模块推断文件名(包括路径)。据我所知,这将适用于您列出的所有情况:
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
回答 2
#!/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__
基于解决方案会在这里失败)。如果脚本位于可执行zip文件(/卵)中,则该脚本有效。如果脚本是PYTHONPATH=/path/to/library.zip python -mscript_to_run
从zip文件“导入”()的,则该脚本有效。在这种情况下,它将返回存档路径。如果脚本被编译成独立的可执行文件(sys.frozen
),它将起作用。它适用于符号链接(realpath
消除了符号链接)。它在交互式解释器中工作;在这种情况下,它将返回当前的工作目录。
回答 3
在Python 3.4+中,您可以使用更简单的pathlib
模块:
from inspect import currentframe, getframeinfo
from pathlib import Path
filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent
回答 4
该os.path...
方法是Python 2中的“完成的事情”。
在Python 3中,您可以找到脚本目录,如下所示:
from pathlib import Path
cwd = Path(__file__).parents[0]
回答 5
只需使用os.path.dirname(os.path.abspath(__file__))
并非常仔细地检查使用情况下是否真的需要exec
。如果您不能将脚本用作模块,则可能是设计麻烦的迹象。
请记住,Python#8的Zen,如果您认为有一个必须在其中使用的用例exec
,那么请告诉我们有关问题背景的更多详细信息。
回答 6
将
import os
cwd = os.getcwd()
你想做什么?我不确定“当前脚本目录”到底是什么意思。您给出的用例的预期输出是什么?
回答 7
首先..如果我们正在讨论注入匿名代码的方式,那么这里有几个用例。
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语句的受限版本。在此受限环境中执行的代码只能访问被认为安全的模块和功能;您可以根据需要为RExec子类添加或删除功能。
但是,如果这更多是学术上的追求,那么您可以通过以下两种愚蠢的方法来进行深入研究。
示例脚本:
./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
当然,这是一种占用大量资源的方法,您会跟踪所有代码。效率不是很高。但是,我认为这是一种新颖的方法,因为即使您深入巢穴,它仍然可以继续工作。您无法覆盖“评估”。虽然可以覆盖execfile()。
注意,此方法仅覆盖exec / execfile,而不覆盖“ import”。对于更高级别的“模块”负载挂钩,您可能可以使用 sys.path_hooks(由PyMOTW致谢)。
多数民众赞成在我的头上。
回答 8
这是一个部分解决方案,仍然比到目前为止所有已发布的解决方案都要好。
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()
用来更改当前目录,则此操作也会失败。
笔记:
sys.argv[0]
将无法正常工作,-c
如果您使用以下命令执行脚本,则会返回python -c "execfile('path-tester.py')"
- 我在https://gist.github.com/1385555发布了完整的测试,欢迎您进行改进。
回答 9
这在大多数情况下应该有效:
import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
回答 10
希望这会有所帮助:-如果您从任何地方运行脚本/模块,您将能够访问__file__
变量,该变量是表示脚本位置的模块变量。
另一方面,如果您使用的是解释器,则您无权访问该变量,否则您将在其中获得名称,NameError
并且os.getcwd()
如果从其他位置运行文件,则将给您错误的目录。
在所有情况下,此解决方案都应为您提供所需的信息:
from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))
我尚未对其进行全面测试,但是它解决了我的问题。