如何正确确定当前脚本目录?
我想看看确定 Python 中当前脚本目录的最佳方法是什么。
我发现,由于调用Python代码的方式多种多样,很难找到一个好的解决方案。
这里有一些问题:
- 如果使用
exec
执行脚本,则未定义__file__
, - 仅定义了
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()
从另一个脚本执行脚本
警告
任何使用当前目录的解决方案都将失败,这可能会根据脚本的调用方式而有所不同,也可能会在运行的脚本中进行更改。 。
I would like to see what is the best way to determine the current script directory in Python.
I discovered that, due to the many ways of calling Python code, it is hard to find a good solution.
Here are some problems:
__file__
is not defined if the script is executed withexec
,execfile
__module__
is defined only in modules
Use cases:
./myfile.py
python myfile.py
./somedir/myfile.py
python somedir/myfile.py
execfile('myfile.py')
(from another script, that can be located in another directory and that can have another current directory.
I know that there is no perfect solution, but I'm looking for the best approach that solves most of the cases.
The most used approach is os.path.dirname(os.path.abspath(__file__))
but this really doesn't work if you execute the script from another one with exec()
.
Warning
Any solution that uses current directory will fail, this can be different based on the way the script is called or it can be changed inside the running script.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(16)
首先..如果我们谈论注入匿名代码的方法,这里缺少一些用例..
但是,真正的问题是,您的目标是什么 - 您是否试图强制执行某种安全性?或者您只是对正在加载的内容感兴趣。
如果您对安全性感兴趣,那么通过 exec/execfile 导入的文件名是无关紧要的 -你应该使用 rexec,它提供以下功能:
然而,如果这更多的是一种学术追求......这里有一些愚蠢的方法,你可以
也许能够更深入地挖掘..
示例脚本:
./deep.py
./deeper.py
/tmp/deepest.py >
./codespy.py
输出
当然,这是一种资源密集型的方法,你需要跟踪
你所有的代码..效率不高。但是,我认为这是一种新颖的方法
因为即使您深入巢穴,它仍然会继续工作。
您不能覆盖“eval”。尽管您可以重写execfile()。
请注意,此方法仅涵盖 exec/execfile,而不是“导入”。
对于更高级别的“模块”负载挂钩,您可以使用 use
sys.path_hooks(写入由 PyMOTW 提供)。
这就是我的全部想法。
First.. a couple missing use-cases here if we're talking about ways to inject anonymous code..
But, the real question is, what is your goal - are you trying to enforce some sort of security? Or are you just interested in whats being loaded.
If you're interested in security, the filename that is being imported via exec/execfile is inconsequential - you should use rexec, which offers the following:
However, if this is more of an academic pursuit.. here are a couple goofy approaches that you
might be able to dig a little deeper into..
Example scripts:
./deep.py
./deeper.py
/tmp/deepest.py
./codespy.py
Output
Of course, this is a resource-intensive way to do it, you'd be tracing
all your code.. Not very efficient. But, I think it's a novel approach
since it continues to work even as you get deeper into the nest.
You can't override 'eval'. Although you can override execfile().
Note, this approach only coveres exec/execfile, not 'import'.
For higher level 'module' load hooking you might be able to use use
sys.path_hooks (Write-up courtesy of PyMOTW).
Thats all I have off the top of my head.
这是一个部分解决方案,仍然比迄今为止所有已发布的解决方案更好。
现在这适用于所有调用,但如果有人使用 chdir() 更改当前目录,这也会失败。
注意:
sys.argv[0]
不起作用,如果使用python -c "execfile('path- tester.py')"
Here is a partial solution, still better than all published ones so far.
Now this works will all calls but if someone use
chdir()
to change the current directory, this will also fail.Notes:
sys.argv[0]
is not going to work, will return-c
if you execute the script withpython -c "execfile('path-tester.py')"
这在大多数情况下应该有效:
This should work in most cases:
希望这有帮助:-
如果您从任何地方运行脚本/模块,您将能够访问 __file__ 变量,它是表示脚本位置的模块变量。
另一方面,如果您使用解释器,则无法访问该变量,您将在其中获得名称
NameError
和os.getcwd()
如果您从其他地方运行该文件,将为您提供不正确的目录。这个解决方案应该可以为您提供在所有情况下所需的内容:
我还没有彻底测试它,但它解决了我的问题问题。
Hopefully this helps:-
If you run a script/module from anywhere you'll be able to access the
__file__
variable which is a module variable representing the location of the script.On the other hand, if you're using the interpreter you don't have access to that variable, where you'll get a name
NameError
andos.getcwd()
will give you the incorrect directory if you're running the file from somewhere else.This solution should give you what you're looking for in all cases:
I haven't thoroughly tested it but it solved my problem.
如果 __file__ 可用:
对于那些我们希望能够从解释器运行命令或获取运行脚本的位置的路径的人:
这可以从解释器运行。
但是当在脚本中运行(或导入)时,它给出了所在位置的路径
您运行脚本的位置,而不是包含的目录的路径
带有打印的脚本。
示例:
如果您的目录结构
为
输出为:
If
__file__
is available:For those we want to be able to run the command from the interpreter or get the path of the place you're running the script from:
This works from the interpreter.
But when run in a script (or imported) it gives the path of the place where
you ran the script from, not the path of directory containing
the script with the print.
Example:
If your directory structure is
with
The output is:
由于之前的答案要求您导入一些模块,我想我会写一个不需要的答案。如果您不想导入任何内容,请使用下面的代码。
如果脚本位于
/path/to/script.py
上,那么这将打印/path/to
。请注意,这将在终端上抛出错误,因为没有执行任何文件。这基本上从__file__
解析目录,删除它的最后一部分。在这种情况下,删除/script.py
以生成输出/path/to
。As previous answers require you to import some module, I thought that I would write one answer that doesn't. Use the code below if you don't want to import anything.
If the script is on
/path/to/script.py
then this would print/path/to
. Note that this will throw error on terminal as no file is executed. This basically parse the directory from__file__
removing the last part of it. In this case/script.py
is removed to produce the output/path/to
.这确实是你能得到的最好的。
使用
exec
/execfile
执行脚本是不常见的;通常您应该使用模块基础结构来加载脚本。如果您必须使用这些方法,我建议在传递给脚本的globals
中设置__file__
,以便它可以读取该文件名。没有其他方法可以获取执行代码中的文件名:正如您所注意到的,CWD 可能位于完全不同的位置。
is indeed the best you're going to get.
It's unusual to be executing a script with
exec
/execfile
; normally you should be using the module infrastructure to load scripts. If you must use these methods, I suggest setting__file__
in theglobals
you pass to the script so it can read that filename.There's no other way to get the filename in execed code: as you note, the CWD may be in a completely different place.
如果您确实想涵盖通过 execfile(...) 调用脚本的情况,您可以使用
inspect
模块来推断文件名(包括路径) 。据我所知,这适用于您列出的所有情况:If you really want to cover the case that a script is called via
execfile(...)
, you can use theinspect
module to deduce the filename (including the path). As far as I am aware, this will work for all cases you listed:在 Python 3.4+ 中,您可以使用更简单的
pathlib
模块:您还可以使用 __file__(如果可用)来完全避免使用
inspect
模块:In Python 3.4+ you can use the simpler
pathlib
module:You can also use
__file__
(when it's available) to avoid theinspect
module altogether:它适用于 CPython、Jython、Pypy。如果使用
execfile()
执行脚本,它就可以工作(基于sys.argv[0]
和__file__
的解决方案将在此处失败)。如果脚本位于可执行 zip 文件 (/an Egg) 中,则它可以工作。如果脚本是从 zip 文件“导入”(PYTHONPATH=/path/to/library.zip python -mscript_to_run
)的,则它可以工作;在这种情况下它返回存档路径。如果脚本被编译成独立的可执行文件 (sys.frozen
),它就可以工作。它适用于符号链接(realpath
消除了符号链接)。它在交互式解释器中工作;在这种情况下它返回当前工作目录。It works on CPython, Jython, Pypy. It works if the script is executed using
execfile()
(sys.argv[0]
and__file__
-based solutions would fail here). It works if the script is inside an executable zip file (/an egg). It works if the script is "imported" (PYTHONPATH=/path/to/library.zip python -mscript_to_run
) from a zip file; it returns the archive path in this case. It works if the script is compiled into a standalone executable (sys.frozen
). It works for symlinks (realpath
eliminates symbolic links). It works in an interactive interpreter; it returns the current working directory in this case.os.path...
方法是 Python 2 中的“完成的事情”。在 Python 3 中,您可以按如下所示找到脚本目录:
The
os.path...
approach was the 'done thing' in Python 2.In Python 3, you can find directory of script as follows:
注意:这个答案现在是一个包(也具有安全的相对导入功能)
https://github.com/heetbeet/locate
对于
.py
脚本以及交互式使用:我经常使用脚本的目录(用于访问与它们一起存储的文件),但我也经常在交互式 shell 中运行这些脚本调试目的。我将
this_dir
定义为:.py
文件时,文件的基目录。这始终是正确的道路。.ipyn
笔记本时,当前工作目录。这始终是正确的路径,因为 Jupyter 将工作目录设置为 .ipynb 基目录。Python 3.4(及更高版本):
Python 2(及更高版本):
说明:
globals()
以字典形式返回所有全局变量。.get("__file__", "./_")
返回键"__file__"
中的值(如果它存在于globals()
中) ,否则返回提供的默认值"./_"
。__file__
(或"./_"
)扩展为绝对文件路径,然后返回文件路径的基目录。替代方案:
如果您确定
__file__
可用于您周围的代码,您可以简化为:this_dir = Path(__file__ ).absolute().parent
this_dir = os.path.dirname(os.path.abspath(__file__))
Note: this answer is now a package (also with safe relative importing capabilities)
https://github.com/heetbeet/locate
For
.py
scripts as well as interactive usage:I frequently use the directory of my scripts (for accessing files stored alongside them), but I also frequently run these scripts in an interactive shell for debugging purposes. I define
this_dir
as:.py
file, the file's base directory. This is always the correct path..ipyn
notebook, the current working directory. This is always the correct path, since Jupyter sets the working directory as the.ipynb
base directory.Python 3.4 (and above):
Python 2 (and above):
Explanation:
globals()
returns all the global variables as a dictionary..get("__file__", "./_")
returns the value from the key"__file__"
if it exists inglobals()
, otherwise it returns the provided default value"./_"
.__file__
(or"./_"
) into an absolute filepath, and then returns the filepath's base directory.Alternative:
If you know for certain that
__file__
is available to your surrounding code, you can simplify to this:this_dir = Path(__file__).absolute().parent
this_dir = os.path.dirname(os.path.abspath(__file__))
会
做你想做的事吗?我不确定“当前脚本目录”到底是什么意思。您给出的用例的预期输出是什么?
Would
do what you want? I'm not sure what exactly you mean by the "current script directory". What would the expected output be for the use cases you gave?
只需使用 os.path.dirname(os.path.abspath(__file__)) 并仔细检查是否确实需要使用 exec 的情况。如果您无法将脚本用作模块,这可能是设计有问题的迹象。
请记住 Python #8 的禅宗,如果您认为对于必须适用于
exec
的用例有一个很好的论据,那么请让我们知道一些有关问题背景的更多详细信息。Just use
os.path.dirname(os.path.abspath(__file__))
and examine very carefully whether there is a real need for the case whereexec
is used. It could be a sign of troubled design if you are not able to use your script as a module.Keep in mind Zen of Python #8, and if you believe there is a good argument for a use-case where it must work for
exec
, then please let us know some more details about the background of the problem.要获取包含当前脚本的目录的绝对路径,您可以使用:
请注意,需要
.resolve()
调用,因为这是使路径成为绝对路径的调用。如果没有resolve()
,您将获得类似于'.'
的内容。此解决方案使用
pathlib
,它是 Python 的一部分stdlib 自 v3.4 (2014) 起。与使用 os 的其他解决方案相比,这是更可取的。官方
pathlib
文档有一个有用的表,将旧的os
函数映射到新函数:https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in -操作系统模块To get the absolute path to the directory containing the current script you can use:
Please note the
.resolve()
call is required, because that is the one making the path absolute. Withoutresolve()
, you would obtain something like'.'
.This solution uses
pathlib
, which is part of Python's stdlib since v3.4 (2014). This is preferrable compared to other solutions usingos
.The official
pathlib
documentation has a useful table mapping the oldos
functions to the new ones: https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module