首页 文章

为什么人们在Python脚本的第一行写#!/ usr / bin / env python shebang?

提问于
浏览
871

在我看来,如果没有该行,文件运行相同 .

19 回答

  • 19

    它只是指定您要使用的解释器 . 要理解这一点,请通过 touch test.py 在终端创建一个文件,然后在该文件中键入以下内容:

    #!/usr/bin/env python3
    print "test"
    

    并执行 chmod +x test.py 以使您的脚本可执行 . 在此之后,当您执行 ./test.py 时,您应该收到错误消息:

    File "./test.py", line 2
        print "test"
                   ^
    SyntaxError: Missing parentheses in call to 'print'
    

    因为python3不支持print运算符 .

    现在继续将代码的第一行更改为:

    #!/usr/bin/env python2
    

    并且它会工作,将 test 打印到stdout,因为python2支持print运算符 . 所以,现在你已经学会了如何在脚本解释器之间切换 .

  • 4

    您可以使用virtualenv尝试此问题

    这是test.py

    #! /usr/bin/env python
    import sys
    print(sys.version)
    

    创建虚拟环境

    virtualenv test2.6 -p /usr/bin/python2.6
    virtualenv test2.7 -p /usr/bin/python2.7
    

    激活每个环境,然后检查差异

    echo $PATH
    ./test.py
    
  • 230

    The exec system call of the Linux kernel understands shebangs (#!) natively

    当你在bash上做:

    ./something
    

    在Linux上,它使用路径 ./something 调用 exec 系统调用 .

    在传递给 exec 的文件上调用内核的这一行:https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

    if((bprm-> buf [0]!='#')||(bprm-> buf [1]!='!'))

    这将读取文件的第一个字节,并将它们与 #! 进行比较 .

    如果这是真的,则Linux内核将解析该行的其余部分,这将使用路径 /usr/bin/env python 和当前文件作为第一个参数进行另一个exec调用:

    /usr/bin/env python /path/to/script.py
    

    这适用于使用 # 作为注释字符的任何脚本语言 .

    是的,您可以通过以下方式进行无限循环:

    printf '#!/a\n' | sudo tee /a
    sudo chmod +x /a
    /a
    

    Bash识别错误:

    -bash: /a: /a: bad interpreter: Too many levels of symbolic links
    

    #! 恰好是人类可读的,但这不是必需的 .

    如果文件以不同的字节开头,则 exec 系统调用将使用不同的处理程序 . 另一个最重要的内置处理程序是用于ELF可执行文件:https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305,它检查字节 7f 45 4c 46 (对于 .ELF 来说也恰好是人类可读的) . 这将读取ELF文件,将其正确放入内存,然后使用它启动新进程 . 另见:How does kernel get an executable binary file running under linux?

    最后,您可以使用 binfmt_misc 机制添加自己的shebang处理程序 . 例如,您可以添加custom handler for .jar files . 此机制甚至通过文件扩展名支持处理程序 . 另一个应用是transparently run executables of a different architecture with QEMU .

    我不认为POSIX指定shebangs:https://unix.stackexchange.com/a/346214/32558,虽然它确实提到了基本原理部分,并且形式为"if executable scripts are supported by the system something may happen" .

  • 139

    强调一个最错过的东西可能是有道理的,这可能会妨碍立即理解 . 在终端中键入 python 时,通常不提供完整路径 . 相反,可执行文件在 PATH 环境变量中查找 . 反过来,当你想直接执行Python程序时,必须告诉shell使用什么解释器(通过hashbang,其他贡献者在上面解释了什么) .

    Hashbang expects full path 给翻译 . 因此,要直接运行Python程序,您必须提供Python二进制文件的完整路径,这种路径会有很大差异,特别是考虑到使用virtualenv . 为了解决可移植性问题,使用 /usr/bin/env . 后者最初旨在就地改变环境并在其中运行命令 . 如果没有提供更改,它会在当前环境中运行命令,这有效地导致了相同的 PATH 查找功能 .

    Source from unix stackexchange

  • 9

    也许你的问题就是这个意义:

    如果你想使用: $python myscript.py

    你根本不需要那条线 . 系统将调用python,然后python interpreter将运行您的脚本 .

    但如果你打算使用: $./myscript.py

    像普通程序或bash脚本一样直接调用它,你需要编写该行来指定系统使用哪个程序来运行它(并且还可以使用 chmod 755 使其可执行)

  • 38

    当你有多个版本的python时,它告诉解释器运行程序的python版本 .

  • 10

    这告诉脚本python目录在哪里!

    #! /usr/bin/env python
    
  • 80

    在其他答案上稍微扩展一下,这里有一个小例子,说明你的命令行脚本如何通过不谨慎地使用 /usr/bin/env shebang行来解决问题:

    $ /usr/local/bin/python -V
    Python 2.6.4
    $ /usr/bin/python -V
    Python 2.5.1
    $ cat my_script.py 
    #!/usr/bin/env python
    import json
    print "hello, json"
    $ PATH=/usr/local/bin:/usr/bin
    $ ./my_script.py 
    hello, json
    $ PATH=/usr/bin:/usr/local/bin
    $ ./my_script.py 
    Traceback (most recent call last):
      File "./my_script.py", line 2, in <module>
        import json
    ImportError: No module named json
    

    Python 2.5中不存在json模块 .

    防止出现此类问题的一种方法是使用通常与大多数Pythons一起安装的版本化python命令名称:

    $ cat my_script.py 
    #!/usr/bin/env python2.6
    import json
    print "hello, json"
    

    如果您只需要区分Python 2.x和Python 3.x,Python 3的最新版本也提供 python3 名称:

    $ cat my_script.py 
    #!/usr/bin/env python3
    import json
    print("hello, json")
    
  • 942

    这意味着更多的历史信息而不是“真正的”答案 .

    请记住,当天你有很多像操作系统一样的unix,其设计师都有自己的东西放置的概念,有时根本不包括Python,Perl,Bash或许多其他GNU /开源的东西 .

    不同的Linux发行版甚至都是如此 . 在Linux上 - 前FHS [1] - 你可能在/ usr / bin /或/ usr / local / bin /中有python . 或者它可能不是安装,所以你 Build 了自己的,并把它放在〜/ bin

    Solaris是我工作过的最糟糕的,部分是从Berkeley Unix到System V的过渡 . 你可以在/ usr /,/ usr / local /,/ usr / ucb,/ opt /等中找到东西 . 这可能会使对于一些非常漫长的道路 . 我记得Sunfreeware.com安装每个软件包的东西's own directory, but I can' t回想一下它是否将二进制文件符号链接到/ usr / bin .

    哦,有时/ usr / bin在NFS服务器上[2] .

    所以开发了 env 实用程序来解决这个问题 .

    然后你可以写 #!/bin/env interpreter 并且只要路径合适,事情就有合理的可能性 . 当然,合理的意思是(对于Python和Perl)你也设置了适当的环境变量 . 对于bash / ksh / zsh它只是起作用 .

    这很重要,因为人们正在传递shell脚本(比如perl和python),如果你在Red Hat Linux工作站上硬编码/ usr / bin / python,它就会在SGI上破坏......好吧,没有,我认为IRIX把python放在了正确的位置 . 但是在Sparc站上它根本不会运行 .

    我想念我的sparc站 . 但不是很多 . 好的,现在你让我在E-Bay上乱逛 . Bastages .

    [1]文件系统层次结构标准 . https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

    [2]是的,有时人们仍然会这样做 . 不,我的腰带上没有萝卜或洋葱 .

  • 38

    这是建议的方式,在文档中提出:

    2.2.2 . 可执行的Python脚本在BSD'ish Unix系统上,Python脚本可以直接执行,就像shell脚本一样,通过放置#行! / usr / bin / env python3.2

    来自http://docs.python.org/py3k/tutorial/interpreter.html#executable-python-scripts

  • -8

    从技术上讲,在Python中,这只是一个注释行 .

    仅当您从shell(从命令行)运行py脚本时才使用此行 . 这被称为"Shebang!",它用于各种情况,而不仅仅是Python脚本 .

    在这里,它指示shell启动特定版本的Python(以处理文件的其余部分) .

  • 4

    这是一个shell约定,告诉shell哪个程序可以执行脚本 .

    #!/usr/bin/env python
    

    解析为Python二进制文件的路径 .

  • 12

    在我看来,如果没有该行,文件运行相同 .

    如果是这样,那么也许你在Windows上运行Python程序? Windows不使用该行,而是使用文件扩展名来运行与文件扩展名关联的程序 .

    However 在2011年,开发了一个"Python launcher"(在某种程度上)模仿Windows的这种Linux行为 . 这仅限于选择运行哪个Python解释器 - 例如在安装了两者的系统上选择Python 2和Python 3 . 启动器可选地通过Python安装安装为 py.exe ,并且可以与 .py 文件关联,以便启动器检查该行,然后启动指定的Python解释器版本 .

  • 5

    为了运行python脚本,我们需要告诉shell三件事:

    • 该文件是脚本

    • 我们想要执行脚本的解释器

    • 所述翻译的路径

    shebang #! 完成(1.) . shebang以 # 开头,因为 # 字符是许多脚本语言中的注释标记 . 因此,解释器会自动忽略shebang行的内容 .

    env 命令完成(2.)和(3.) . 引用"grawity,"

    env命令的一个常见用途是启动解释器,利用env将搜索$ PATH以获取它被命令启动的命令 . 由于shebang行需要指定绝对路径,并且由于各种解释器(perl,bash,python)的位置可能变化很大,因此通常使用:#!/ usr / bin / env perl而不是尝试猜猜它是/ bin / perl,/ usr / bin / perl,/ usr / local / bin / perl,/ usr / local / pkg / perl,/ fileserver / usr / bin / perl,还是/ home / MrDaniel / usr用户系统上的/ bin / perl ...另一方面,env几乎总是在/ usr / bin / env中 . (除非它不是;某些系统可能使用/ bin / env,但这是一个相当罕见的情况,只发生在非Linux系统上 . )

  • 8

    如果您在虚拟环境中运行脚本,例如 venv ,那么在处理 venv 时执行 which python 将显示Python解释器的路径:

    ~/Envs/venv/bin/python

    请注意,Python解释器路径中的 name of the virtual environment is embedded . 因此,在脚本中对此路径进行硬编码将导致两个问题:

    • 如果将脚本上载到存储库,则为 forcing other users to have the same virtual environment name . 这是他们首先确定问题 .

    • won't be able to run the script across multiple virtual environments 即使您在其他虚拟环境中拥有所有必需的软件包 .

    因此,为了增加_75585的答案,理想的shebang是 #!/usr/bin/env python ,不仅仅是跨操作系统的可移植性,而且还是跨虚拟环境的可移植性!

  • 3

    这被称为shebang line . 作为Wikipedia entry explains

    在计算中,shebang(也称为hashbang,hashpling,pound bang或crunchbang)指的是字符“#!”当它们是解释器指令中的前两个字符作为文本文件的第一行时 . 在类Unix操作系统中,程序加载器将这两个字符作为文件是脚本的指示,并尝试使用文件中第一行其余部分指定的解释器来执行该脚本 .

    另见Unix FAQ entry .

    即使在Windows上,shebang行也没有确定要运行的解释器,您可以通过在shebang行上指定它们来将选项传递给解释器 . 我发现在一次性脚本中保留一个通用的shebang行很有用(例如我在回答问题时写的那些),所以我可以在Windows和_75513上快速测试它们 .

    env utility允许您在路径上调用命令:

    第一个剩余参数指定要调用的程序名称;根据PATH环境变量搜索它 . 任何剩余的参数都作为参数传递给该程序 .

  • 2

    这样做的主要原因是使脚本可以跨操作系统环境移植 .

    例如,在mingw下,python脚本使用:

    #!/c/python3k/python
    

    在GNU / Linux发行版下,它是:

    #!/usr/local/bin/python
    

    要么

    #!/usr/bin/python
    

    在所有(OS / X)的最佳商业Unix sw / hw系统下,它是:

    #!/Applications/MacPython 2.5/python
    

    或者在FreeBSD上:

    #!/usr/local/bin/python
    

    但是,所有这些差异可以通过使用以下方式使脚本可移植到所有

    #!/usr/bin/env python
    
  • 34

    如果您安装了多个版本的Python, /usr/bin/env 将确保使用的解释器是您环境 $PATH 上的第一个 . 替代方案是硬编码像 #!/usr/bin/python ;没关系,但不太灵活 .

    在Unix中,一个可以被解释的可执行文件可以通过在第一行的开头使用 #! ,然后是解释器(以及它可能需要的任何标志)来指示要使用的解释器 .

    当然,如果您正在谈论其他平台,则此规则不适用(但是"shebang line"没有任何损害,如果您将该脚本复制到具有Unix基础的平台,例如Linux,Mac等,将会有所帮助) .

  • 25

    考虑到 python2python3 之间的可移植性问题,除非程序与两者兼容,否则应始终指定任一版本 .

    一些发行版现在将 python 符号链接发送到 python3 一段时间 - 不要依赖 pythonpython2 .

    PEP 394强调了这一点:

    为了容忍跨平台的差异,所有需要调用Python解释器的新代码都不应该指定python,而是应该指定python2或python3(或更具体的python2.x和python3.x版本;请参阅迁移说明) . 当从shell脚本调用时,通过system()调用调用时,或者在任何其他上下文中调用时,应该在shebang中进行这种区分 .

相关问题