【问题标题】:Purpose of #!/usr/bin/python3 shebang#!/usr/bin/python3 shebang 的目的
【发布时间】:2011-12-01 23:42:33
【问题描述】:

我在几种脚本语言中都注意到了这一点,但在这个例子中,我使用的是 python。在许多教程中,它们会在第一行以#!/usr/bin/python3 开头。我不明白为什么我们有这个。

  • 操作系统不应该知道它是一个 python 脚本吗(显然它已经安装,因为你正在引用它)
  • 如果用户使用的操作系统不是基于 unix 的,该怎么办
  • 无论出于何种原因,该语言都安装在不同的文件夹中
  • 用户有不同的版本。尤其是当它不是完整版本号时(如 Python3 与 Python32)

如果有的话,由于上面列出的原因,我可以看到这会破坏 python 脚本。

【问题讨论】:

  • 我来这个问题只是为了复制shebang字符串。

标签: python scripting


【解决方案1】:

#!/usr/bin/python3shebang line

shebang 行定义解释器所在的位置。在这种情况下,python3 解释器位于/usr/bin/python3。 shebang 行也可以是bashrubyperl 或任何其他脚本语言的解释器,例如:#!/bin/bash

没有shebang行,操作系统不知道它是一个python脚本,即使你在脚本上设置了执行标志(chmod +x script.py)并像./script.py一样运行它。要使脚本在 python3 中默认运行,请将其调用为 python3 script.py 或设置 shebang 行。

您可以使用#!/usr/bin/env python3 来实现跨不同系统的可移植性,以防它们在不同位置安装了语言解释器。

【讨论】:

  • 所以应该选择#! /usr/bin/env python3 而不是#! /usr/bin/python3?
  • @winklerrr 是的,它的使用范围更广。
  • 设置“执行标志”是什么意思?
  • @Papaya-Automaton chmod +x.
【解决方案2】:

这就是所谓的哈希爆炸。如果您从 shell 运行脚本,它将检查第一行以确定应该启动哪个程序来解释脚本。

非基于 Unix 的操作系统将使用自己的规则来确定如何运行脚本。例如,Windows 将使用文件扩展名,# 将导致第一行被视为注释。

如果 Python 可执行文件的路径错误,那么脚本自然会失败。从标准约定指定的任何位置创建指向实际可执行文件的链接都很容易。

【讨论】:

    【解决方案3】:

    这一行有助于找到将运行脚本的程序可执行文件。这种 shebang 表示法在大多数脚本语言中都是相当标准的(至少在成熟的操作系统上使用)。

    这一行的一个重要方面是指定将使用哪个解释器。例如,在许多以开发为中心的 Linux 发行版上,同时安装多个版本的 python 是很正常的。

    Python 2.x 和 Python 3 并非 100% 兼容,因此这种差异可能非常重要。所以#! /usr/bin/python#! /usr/bin/python3 是不一样的(也和#! /usr/bin/env python3 完全一样,正如本页其他地方所指出的那样。

    【讨论】:

      【解决方案4】:

      3.7 Python doc 中阐明 shebang 线在 Windows 上的工作原理:

      • 如果脚本文件的第一行以#! 开头,则称为“shebang”行。 Linux 和其他类 Unix 操作系统对此类行具有原生支持,并且它们通常在此类系统上用于指示应如何执行脚本。
      • Windows 的 Python 启动器允许在 Windows 上将相同的工具与 Python 脚本一起使用
      • 为了允许 Python 脚本中的 shebang 行在 Unix 和 Windows 之间移植,启动器支持许多“虚拟”命令来指定要使用的解释器。支持的虚拟命令有:
        • /usr/bin/env python
          • shebang 行的 /usr/bin/env 形式还有一个特殊属性。在查找已安装的 Python 解释器之前,此表单将在可执行文件 PATH 中搜索 Python 可执行文件。这对应于执行 PATH 搜索的 Unix env 程序的行为。
        • /usr/bin/python
        • /usr/local/bin/python
        • 蟒蛇

      【讨论】:

        【解决方案5】:
        1. 而这一行是如何

        2. 它被忽略了。

        3. 它将无法运行,应更改为指向正确的位置。或者应该使用env

        4. 它将无法运行,并且可能无论如何都无法在不同的版本下运行。

        【讨论】:

          【解决方案6】:

          其实判断一个文件是什么类型的文件是很复杂的,所以现在操作系统不能只知道。它可以根据 -

          • 分机
          • 尿路感染
          • MIME

          但是命令行并不关心这一切,因为它运行在一个有限的向后兼容层上,从那时起那些花哨的废话就没有任何意义了。如果您确定双击它,现代操作系统可以解决这个问题 - 但如果您从终端运行它,则不会,因为终端不关心您花哨的操作系统特定文件键入 API。

          关于其他方面。很方便,同样可以运行

          python3 path/to/your/script

          如果您的 python 不在指定的路径中,那么它将无法工作,但我们倾向于安装一些东西来使这样的东西工作,而不是相反。如果您在 *nix 下实际上并不重要,是否考虑这一行取决于您的 shell,因为它是 shellcode。所以例如你可以在 Windows 下运行bash

          你实际上可以完全省略这一行,它只是意味着调用者必须指定一个解释器。也不要将您的解释器放在非标准位置,然后尝试在不提供解释器的情况下调用脚本。

          【讨论】:

            【解决方案7】:

            Linux 内核的exec 系统调用本机理解shebangs (#!)

            当你在 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/python3 和当前文件作为第一个参数进行另一个 exec 调用:

            /usr/bin/python3 /path/to/script.py
            

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

            类似地,如果您决定改用env,您可能应该始终这样做,以便在python3 位于不同位置的系统上工作,特别是pyenv,另请参阅this question,shebang :

            #!/usr/bin/env python3
            

            以类似方式结束调用:

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

            这符合您对env python3 的期望:在PATH 中搜索python3 并运行/usr/bin/python3 /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,它也恰好是人类可读的)。让我们通过读取 /bin/ls 的前 4 个字节来确认,这是一个 ELF 可执行文件:

            head -c 4 "$(which ls)" | hd 
            

            输出:

            00000000  7f 45 4c 46                                       |.ELF|
            00000004                                                                 
            

            因此,当内核看到这些字节时,它会获取 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,尽管它确实在基本原理部分中提到,并且以“如果系统支持可执行脚本可能会发生某些事情”的形式。然而,macOS 和 FreeBSD 似乎也实现了它。

            PATH搜索动机

            可能,shebangs 存在的一大动机是,在 Linux 中,我们经常希望从 PATH 运行命令,就像:

            basename-of-command
            

            代替:

            /full/path/to/basename-of-command
            

            但是,如果没有 shebang 机制,Linux 怎么知道如何启动每种类型的文件?

            在命令中硬编码扩展:

             basename-of-command.py
            

            或在每个解释器上实现 PATH 搜索:

            python3 basename-of-command
            

            这是可能的,但是如果我们决定将命令重构为另一种语言,那么这会导致一切都中断。

            Shebangs 很好地解决了这个问题。

            另见:Why do people write #!/usr/bin/env python on the first line of a Python script?

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-05-29
              • 1970-01-01
              • 2022-01-12
              • 2019-01-20
              • 1970-01-01
              • 2021-11-09
              • 2018-02-15
              • 2017-10-03
              相关资源
              最近更新 更多