如何正确确定当前脚本目录?

发布于 2024-09-19 16:10:41 字数 720 浏览 16 评论 0原文

我想看看确定 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 with exec, 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(16

遗心遗梦遗幸福 2024-09-26 16:10:42

首先..如果我们谈论注入匿名代码的方法,这里缺少一些用例..

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

当然,这是一种资源密集型的方法,你需要跟踪
你所有的代码..效率不高。但是,我认为这是一种新颖的方法
因为即使您深入巢穴,它仍然会继续工作。
您不能覆盖“eval”。尽管您可以重写exe​​cfile()。

请注意,此方法仅涵盖 exec/execfile,而不是“导入”。
对于更高级别的“模块”负载挂钩,您可以使用 use
sys.path_hooks(写入由 PyMOTW 提供)。

这就是我的全部想法。

First.. a couple missing use-cases here if we're talking about ways to inject anonymous code..

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

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:

This module contains the RExec class,
which supports r_eval(), r_execfile(),
r_exec(), and r_import() methods, which
are restricted versions of the standard
Python functions eval(), execfile() and
the exec and import statements. Code
executed in this restricted environment
will only have access to modules and
functions that are deemed safe; you can
subclass RExec add or remove capabilities as
desired.

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

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)

Output

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

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.

何以心动 2024-09-26 16:10:42

这是一个部分解决方案,仍然比迄今为止所有已发布的解决方案更好。

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() 更改当前目录,这也会失败。

注意:

Here is a partial solution, still better than all published ones so far.

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__))

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 with python -c "execfile('path-tester.py')"
  • I published a complete test at https://gist.github.com/1385555 and you are welcome to improve it.
离去的眼神 2024-09-26 16:10:42

这在大多数情况下应该有效:

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

This should work in most cases:

import os,sys
dirname=os.path.dirname(os.path.realpath(sys.argv[0]))
纸短情长 2024-09-26 16:10:42

希望这有帮助:-
如果您从任何地方运行脚本/模块,您将能够访问 __file__ 变量,它是表示脚本位置的模块变量。

另一方面,如果您使用解释器,则无法访问该变量,您将在其中获得名称 NameErroros.getcwd()如果您从其他地方运行该文件,将为您提供不正确的目录。

这个解决方案应该可以为您提供在所有情况下所需的内容:

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

我还没有彻底测试它,但它解决了我的问题问题。

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 and os.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:

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

I haven't thoroughly tested it but it solved my problem.

夏尔 2024-09-26 16:10:42

如果 __file__ 可用:

# -- script1.py --
import os
file_path = os.path.abspath(__file__)
print(os.path.dirname(file_path))

对于那些我们希望能够从解释器运行命令或获取运行脚本的位置的路径的人:

# -- script2.py --
import os
print(os.path.abspath(''))

这可以从解释器运行。
但是当在脚本中运行(或导入)时,它给出了所在位置的路径
您运行脚本的位置,而不是包含的目录的路径
带有打印的脚本。

示例:

如果您的目录结构

test_dir (in the home dir)
├── main.py
└── test_subdir
    ├── script1.py
    └── script2.py

# -- main.py --
import script1.py
import script2.py

输出为:

~/test_dir/test_subdir
~/test_dir

If __file__ is available:

# -- script1.py --
import os
file_path = os.path.abspath(__file__)
print(os.path.dirname(file_path))

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:

# -- script2.py --
import os
print(os.path.abspath(''))

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

test_dir (in the home dir)
├── main.py
└── test_subdir
    ├── script1.py
    └── script2.py

with

# -- main.py --
import script1.py
import script2.py

The output is:

~/test_dir/test_subdir
~/test_dir
尴尬癌患者 2024-09-26 16:10:42

由于之前的答案要求您导入一些模块,我想我会写一个不需要的答案。如果您不想导入任何内容,请使用下面的代码。

this_dir = '/'.join(__file__.split('/')[:-1])
print(this_dir)

如果脚本位于 /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.

this_dir = '/'.join(__file__.split('/')[:-1])
print(this_dir)

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.

橘虞初梦 2024-09-26 16:10:42
print(__import__("pathlib").Path(__file__).parent)
print(__import__("pathlib").Path(__file__).parent)
清泪尽 2024-09-26 16:10:41
os.path.dirname(os.path.abspath(__file__))

这确实是你能得到的最好的。

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

没有其他方法可以获取执行代码中的文件名:正如您所注意到的,CWD 可能位于完全不同的位置。

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

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 the globals 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.

从此见与不见 2024-09-26 16:10:41

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

filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))

If you really want to cover the case that a script is called via execfile(...), you can use the inspect module to deduce the filename (including the path). As far as I am aware, this will work for all cases you listed:

filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
养猫人 2024-09-26 16:10:41

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

from inspect import currentframe, getframeinfo
from pathlib import Path

filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent

您还可以使用 __file__(如果可用)来完全避免使用 inspect 模块:

from pathlib import Path
parent = Path(__file__).resolve().parent

In Python 3.4+ you can use the simpler pathlib module:

from inspect import currentframe, getframeinfo
from pathlib import Path

filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent

You can also use __file__ (when it's available) to avoid the inspect module altogether:

from pathlib import Path
parent = Path(__file__).resolve().parent
小忆控 2024-09-26 16:10:41
#!/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 文件 (/an Egg) 中,则它可以工作。如果脚本是从 zip 文件“导入”(PYTHONPATH=/path/to/library.zip python -mscript_to_run)的,则它可以工作;在这种情况下它返回存档路径。如果脚本被编译成独立的可执行文件 (sys.frozen),它就可以工作。它适用于符号链接(realpath 消除了符号链接)。它在交互式解释器中工作;在这种情况下它返回当前工作目录。

#!/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())

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.

眉目亦如画i 2024-09-26 16:10:41

os.path... 方法是 Python 2 中的“完成的事情”。

在 Python 3 中,您可以按如下所示找到脚本目录:

from pathlib import Path
script_path = Path(__file__).parent

The os.path... approach was the 'done thing' in Python 2.

In Python 3, you can find directory of script as follows:

from pathlib import Path
script_path = Path(__file__).parent
狼性发作 2024-09-26 16:10:41

注意:这个答案现在是一个包(也具有安全的相对导入功能)

https://github.com/heetbeet/locate

$ pip install locate

$ python
>>> from locate import this_dir
>>> print(this_dir())
C:/Users/simon

对于 .py 脚本以及交互式使用:

我经常使用脚本的目录(用于访问与它们一起存储的文件),但我也经常在交互式 shell 中运行这些脚本调试目的。我将 this_dir 定义为:

  • 运行或导入 .py 文件时,文件的基目录。这始终是正确的道路。
  • 运行 .ipyn 笔记本时,当前工作目录。这始终是正确的路径,因为 Jupyter 将工作目录设置为 .ipynb 基目录。
  • 在 REPL 中运行时,当前工作目录。嗯,当代码与文件分离时,实际的“正确路径”是什么?相反,您有责任在调用 REPL 之前更改为“正确的路径”。

Python 3.4(及更高版本):

from pathlib import Path
this_dir = Path(globals().get("__file__", "./_")).absolute().parent

Python 2(及更高版本):

import os
this_dir = os.path.dirname(os.path.abspath(globals().get("__file__", "./_")))

说明:

  • globals() 以字典形式返回所有全局变量。
  • .get("__file__", "./_") 返回键 "__file__" 中的值(如果它存在于 globals() 中) ,否则返回提供的默认值"./_"
  • 其余代码只是将 __file__ (或 "./_")扩展为绝对文件路径,然后返回文件路径的基目录。

替代方案:

如果您确定 __file__ 可用于您周围的代码,您可以简化为:

  • >= Python 3.4this_dir = Path(__file__ ).absolute().parent
  • >= Python 2: 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

$ pip install locate

$ python
>>> from locate import this_dir
>>> print(this_dir())
C:/Users/simon

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:

  • When running or importing a .py file, the file's base directory. This is always the correct path.
  • When running an .ipyn notebook, the current working directory. This is always the correct path, since Jupyter sets the working directory as the .ipynb base directory.
  • When running in a REPL, the current working directory. Hmm, what is the actual "correct path" when the code is detached from a file? Rather, make it your responsibility to change into the "correct path" before invoking the REPL.

Python 3.4 (and above):

from pathlib import Path
this_dir = Path(globals().get("__file__", "./_")).absolute().parent

Python 2 (and above):

import os
this_dir = os.path.dirname(os.path.abspath(globals().get("__file__", "./_")))

Explanation:

  • globals() returns all the global variables as a dictionary.
  • .get("__file__", "./_") returns the value from the key "__file__" if it exists in globals(), otherwise it returns the provided default value "./_".
  • The rest of the code just expands __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:

  • >= Python 3.4: this_dir = Path(__file__).absolute().parent
  • >= Python 2: this_dir = os.path.dirname(os.path.abspath(__file__))
泼猴你往哪里跑 2024-09-26 16:10:41

import os
cwd = os.getcwd()

做你想做的事吗?我不确定“当前脚本目录”到底是什么意思。您给出的用例的预期输出是什么?

Would

import os
cwd = os.getcwd()

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?

穿越时光隧道 2024-09-26 16:10:41

只需使用 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 where exec 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.

愿与i 2024-09-26 16:10:41

要获取包含当前脚本的目录的绝对路径,您可以使用:

from pathlib import Path
absDir = Path(__file__).parent.resolve()

请注意,需要 .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:

from pathlib import Path
absDir = Path(__file__).parent.resolve()

Please note the .resolve() call is required, because that is the one making the path absolute. Without resolve(), 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 using os.

The official pathlib documentation has a useful table mapping the old os functions to the new ones: https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文