获取当前Python脚本的名称

我正在尝试获取当前正在运行的Python脚本的名称。

我有一个名为foo.py的脚本,我想做这样的事情,以获得脚本名称:

print(Scriptname)
480927 次浏览

您可以使用__file__来获取当前文件的名称。当在主模块中使用时,这是最初调用的脚本的名称。

如果希望省略目录部分(可能会出现),可以使用os.path.basename(__file__)

import sys
print(sys.argv[0])

这将为python foo.py打印foo.py,为python dir/foo.py打印dir/foo.py,等等。它是python的第一个参数。(注意,py2exe之后是foo.exe。)

试试这个:

print __file__

注意,__file__将给出代码所在的文件,该文件可以导入,并且与正在解释的主文件不同。要获得主文件,可以使用特殊的__main__模块:

import __main__ as main
print(main.__file__)

请注意,__main__.__file__在Python 2.7中有效,但在3.2中无效,因此使用上面的import-as语法使其可移植。

以上答案很好。但是我发现这个方法使用上面的结果更有效 这将导致实际的脚本文件名而不是路径

import sys
import os
file_name =  os.path.basename(sys.argv[0])

为了完整起见,我认为有必要总结各种可能的结果,并为每种结果的确切行为提供参考。

答案由四个部分组成:

  1. 返回当前执行脚本完整路径的不同方法的列表。

  2. 关于相对路径处理的警告。

  3. 关于符号链接处理的建议。

  4. 一些方法的说明,这些方法可用于从完整的文件路径中提取实际的文件名(带或不带后缀)。


提取完整的文件路径

  • __file__是当前正在执行的文件,详见官方文档:

    __file__是加载模块的文件的路径名,如果它是从一个文件中加载的。对于某些类型的模块,__file__属性可能会丢失,比如静态链接到解释器的C模块;对于从共享库动态加载的扩展模块,它是共享库文件的路径名。

    从< em > Python3.4 < / em >开始,对于发行18416__file__始终是一个绝对路径,除非当前正在执行的文件是一个使用相对路径直接执行的脚本(而不是通过带有-m命令行选项的解释器)。

  • __main__.__file__(需要导入__main__)只是访问前面提到的主模块__file__属性,例如从命令行调用的脚本。

    从< em > Python3.9 < / em >开始,从发行20443开始,__main__模块的__file__属性变成了一个绝对路径,而不是相对路径。

  • sys.argv[0](需要导入sys)是从命令行调用的脚本名称,可能是一个绝对路径,详细信息见官方文档:

    argv[0]是脚本名(是否为完整路径名取决于操作系统)。如果该命令是使用解释器的-c命令行选项执行的,则argv[0]将被设置为字符串'-c'。如果没有传递脚本名称给Python解释器,则argv[0]为空字符串。

    正如在这个问题的另一个答案中提到的,通过< em > py2exe < / em >< em > PyInstaller < / em >等工具转换为独立可执行程序的Python脚本在使用这种方法时可能不会显示所需的结果(即sys.argv[0]将保存可执行文件的名称,而不是该可执行文件中Python主文件的名称)。

  • 如果上述选项都不起作用,可能是由于非典型的执行过程或不规则的导入操作,< em > < / em >模块进行检查可能会被证明是有用的,正如这个问题的另一个答案所建议的那样:

    import inspect
    source_file_path = inspect.getfile(inspect.currentframe())
    

    然而,当在没有Python堆栈框架的实现中运行时,inspect.currentframe ()将引发异常。

    注意,inspect.getfile(…)优先于inspect.getsourcefile(…),因为后者引发TypeError异常只能确定二进制文件,而不能确定相应的源文件(另请参阅这是另一个问题的答案)。

  • Python3.6开始,正如这个问题的另一个答案中详细描述的那样,可以安装一个外部开源库< em > lib_programname < / em >,它是为这个问题量身定制的完整解决方案。

    这个库遍历上面列出的所有方法,直到返回有效路径。如果它们都失败了,就会引发异常。它还尝试解决各种缺陷,例如通过< em > pytest < / em >框架或< em > pydoc < / em >模块进行调用。

    import lib_programname
    # this returns the fully resolved path to the launched python program
    path_to_program = lib_programname.get_path_executed_script()  # type: pathlib.Path
    

处理相对路径

在处理恰好返回相对路径的方法时,可能很容易调用各种路径操作函数,例如os.path.abspath(...)os.path.realpath(...),以便提取完整或真实的路径。

但是,这些方法依赖于当前路径来获得完整路径。因此,如果程序首先更改当前工作目录,例如通过os.chdir(...),然后才调用这些方法,则它们将返回错误的路径。


处理符号链接

如果当前脚本是一个符号链接,那么上述所有操作都将返回符号链接的路径,而不是实际文件的路径,应该调用os.path.realpath(...)来提取后者。


提取实际文件名的进一步操作

os.path.basename(...)可以被调用在上面的任何一个上,以提取实际的文件名,os.path.splitext(...)可以被调用在实际的文件名上,以截断它的后缀,如os.path.splitext(os.path.basename(...))

Python 3.4 < em > < / em >开始,根据PEP 428< em > # EYZ1 < / em >模块< em > # EYZ0 < / em >类也可以用于上述任何一个。具体来说,pathlib.PurePath(...).name提取实际文件名,pathlib.PurePath(...).stem提取不带后缀的实际文件名。

如果你正在做一个不寻常的导入(例如,它是一个选项文件),尝试:

import inspect
print (inspect.getfile(inspect.currentframe()))

注意,这将返回文件的绝对路径。

对于现代Python版本(3.4+),Path(__file__).name应该更加习惯。此外,Path(__file__).stem提供了脚本名称,但不包含.py扩展名。

由于OP要求当前脚本文件的名称,我更喜欢

import os
os.path.split(sys.argv[0])[1]

我们可以尝试这样做,以获得当前的脚本名称没有扩展名。

import os


script_name = os.path.splitext(os.path.basename(__file__))[0]

从Python 3.5开始,你可以简单地做:

from pathlib import Path
Path(__file__).stem

更多信息请点击这里:https://docs.python.org/3.5/library/pathlib.html#pathlib.PurePath.stem

例如,我在我的用户目录下有一个名为test.py的文件,里面是这样的:

from pathlib import Path


print(Path(__file__).stem)
print(__file__)

运行这个输出:

>>> python3.6 test.py
test
test.py

所有的答案都很棒,但也有一些你第一眼可能看不出来的问题。

让我们定义我们想要的-我们想要执行的脚本的名称,而不是当前模块的名称-所以__file__只在执行的脚本中使用,而不是在导入的模块中使用。 sys.argv也是有问题的——如果你的程序是由pytest调用的呢?还是pydoc runner ?或者它是否被uwsgi调用

还有第三种获取脚本名称的方法,我在答案中没有看到-你可以检查堆栈。

另一个问题是,你(或其他程序)可以篡改sys.argv__main__.__file__——它可能存在,也可能不存在。它可能成立,也可能不成立。至少您可以检查脚本(期望的结果)是否存在!

lib_programname库正是这样做的:

  • 检查__main__是否存在
  • 检查__main__.__file__是否存在
  • __main__.__file__一个有效的结果(这个脚本存在吗?)
  • 如果不是:检查sys.argv:
  • 系统中是否存在pytest, docrunner等。argv吗?——比;如果是,请忽略
  • 我们能得到一个有效的结果吗?
  • 如果不是:检查堆栈,并尽可能从那里得到结果
  • 如果堆栈也没有给出有效的结果,则抛出异常。

通过这种方式,我的解决方案是工作到目前为止与setup.py testuwsgipytestpycharm pytestpycharm docrunner (doctest)dreampieeclipse

Dough Hellman也有一篇关于这个问题的不错的博客文章,“从python中确定进程的名称”;

顺便说一句,它将在python 3.9中再次改变:主要模块的文件属性变成了绝对路径,而不是相对路径。在当前目录被os.chdir()更改后,这些路径仍然有效

所以我宁愿照顾一个小模块,而不是浏览我的代码库,如果它应该改变一些…


免责声明:我是lib_programname库的作者。

你可以在不导入操作系统或其他库的情况下做到这一点。

如果你想获取当前python脚本的路径,请使用:__file__

如果你只想得到没有.py扩展名的文件名,使用这个:

__file__.rsplit("/", 1)[1].split('.')[0]