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

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

我想看看确定python中当前脚本目录的最佳方法是什么?

我发现,由于调用python代码的方式很多,很难找到一个好的解决方案。

这里有一些问题:

  • __file__如果脚本与执行没有定义execexecfile
  • __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.


回答 0

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

确实是您将获得的最好的。

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

没有其他方法可以在执行代码中获取文件名:如您所述,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.


回答 1

如果您确实想解决通过调用脚本的情况,则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))

回答 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消除了符号链接)。它在交互式解释器中工作;在这种情况下,它将返回当前的工作目录。

#!/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.


回答 3

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

from inspect import currentframe, getframeinfo
from pathlib import Path

filename = getframeinfo(currentframe()).filename
parent = Path(filename).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__ to avoid the inspect module altogether:

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

回答 4

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

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

from pathlib import Path
cwd = Path(__file__).parents[0]

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
cwd = Path(__file__).parents[0]

回答 5

只需使用os.path.dirname(os.path.abspath(__file__))并非常仔细地检查使用情况下是否真的需要exec。如果您不能将脚本用作模块,则可能是设计麻烦的迹象。

请记住,Python#8的Zen,如果您认为有一个必须在其中使用的用例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.


回答 6

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?


回答 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致谢)。

多数民众赞成在我的头上。

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.


回答 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发布了完整的测试,欢迎您进行改进。

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.

回答 9

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

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

回答 10

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

另一方面,如果您使用的是解释器,则您无权访问该变量,否则您将在其中获得名称,NameError并且os.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.