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

时间:2010-09-15 14:34:57

标签: python pythonpath dirname

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

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

以下是一些问题:

    如果脚本是使用__file__执行的,则{li> 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()从另一个脚本执行脚本,这实际上不起作用。

警告

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

16 个答案:

答案 0 :(得分:182)

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

确实是你最好的。

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

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

答案 1 :(得分:115)

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

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

答案 2 :(得分:40)

#!/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__的解决方案将在此处失败),则它可以正常工作。如果脚本在an executable zip file (/an egg)内,它可以工作。如果脚本是从zip文件“导入”(PYTHONPATH=/path/to/library.zip python -mscript_to_run),它可以工作;在这种情况下,它返回归档路径。如果将脚本编译为独立可执行文件(sys.frozen),则它可以正常工作。它适用于符号链接(realpath消除了符号链接)。它适用于交互式口译员;在这种情况下,它返回当前的工作目录。

答案 3 :(得分:22)

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

from inspect import currentframe, getframeinfo
from pathlib import Path

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

答案 4 :(得分:6)

df.loc[(df["D"] == "Monday") & (df["B"] - df["C"]).dt.days.isin({-7, -8})] # A B C D # 1 aeg 2012-12-28 2013-01-04 Monday # 4 feh 2012-07-26 2012-08-03 Monday # 5 dd 2012-06-22 2012-06-30 Monday 方法是做过的事情'在Python 2中。

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

os.path...

答案 5 :(得分:5)

只需使用os.path.dirname(os.path.abspath(__file__))并仔细检查是否真的需要使用exec的情况。如果您无法将脚本用作模块,则可能是设计有问题的迹象。

请记住 Python#8 的Zen,如果您认为对于exec必须有用的用例有一个很好的论据,那么请告诉我们一些有关问题背景的更多细节。

答案 6 :(得分:3)

首先..如果我们正在讨论注入匿名代码的方法,那么这里有几个用例丢失了。

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

但是,真正的问题是,你的目标是什么 - 你是否试图强制实施某种安全措施?或者你只是对装载什么感兴趣。

如果你对security感兴趣,那么通过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'。虽然可以覆盖execfile()。

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

这就是我所有的一切。

答案 7 :(得分:2)

import os
cwd = os.getcwd()

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

答案 8 :(得分:2)

这是一个部分解决方案,仍然比目前所有发布的解决方案都要好。

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 :(得分:2)

这在大多数情况下都适用:

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

答案 10 :(得分:1)

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

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

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

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

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

答案 11 :(得分:1)

注意:此答案现在是软件包

import { Config } from "../config";
import Link from "next/link";
import useFetch from "../hooks/useFetch";

    export default function MainNav() {
      const [links, isLoading] = useFetch(`${Config.apiUrl}/wp-json/menus/v1/menus/main-nav`);

      if(isLoading) {
        return <Spinner/>
      }
    
      return (
        <nav>
          {!!links && !isLoading &&
            links.map((link) => (
              <Link href="/">
                <a>{link.title}</a>
              </Link>
            ))}
        </nav>
      );
    }

对于$ pip install locate >>> from locate import this_dir >>> print(this_dir()) C:/Users/Heetbeet 脚本以及交互式用法:

我经常使用脚本的目录(用于访问脚本旁边存储的文件),但是我也经常在交互式外壳中运行这些脚本以进行调试。我将.py定义为:

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

Python 3.4(及更高版本):

.ipynb

Python 2(及更高版本):

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

说明:

  • import os __dirpath__ = os.path.dirname(os.path.abspath(globals().get("__file__", "./_"))) 将所有全局变量作为字典返回。
  • globals()从键.get("__file__", "./_")返回值(如果键"__file__"中存在),否则返回提供的默认值globals()
  • 其余代码仅将"./_"(或__file__)扩展为绝对文件路径,然后返回文件路径的基本目录。

答案 12 :(得分:1)

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

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

请注意 .resolve() 调用是必需的,因为它使路径成为绝对路径。如果没有 resolve(),您将获得类似 '.' 的内容。

此解决方案使用 pathlib,它自 v3.4 (2014) 起成为 Python 标准库的一部分。与使用 os 的其他解决方案相比,这是更可取的。

官方 pathlib 文档有一个有用的表,将旧的 os 函数映射到新的函数:https://docs.python.org/3/library/pathlib.html#correspondence-to-tools-in-the-os-module

答案 13 :(得分:0)

如果__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

答案 14 :(得分:-2)

print(__import__("pathlib").Path(__file__).parent)

答案 15 :(得分:-6)

pwd
在jupyter笔记本上:pwd + Shift + Enter
在spyder处:pwd + F9