【问题标题】:Why should the shebang line always be the first line?为什么shebang线总是第一线?
【发布时间】:2012-10-06 07:40:02
【问题描述】:

我有一个简单的 perl 脚本如下:

#!/usr/bin/perl

use strict;
use warnings;

print "hello world!\n";

我可以如下执行这个脚本:

>temp.pl
hello world!
>

如果我像这样添加一些 cmets:

#this script is just for test
#the shebang
#!/usr/bin/perl

use strict;
use warnings;

print "hello world!\n";

当我尝试执行时,它给我的输出如下:

> temp.pl
use: Command not found.
use: Command not found.
print: Command not found.
> 

这里的重点是,无论如何,shebang 线都应该始终位于顶部。 谁能解释一下为什么?

【问题讨论】:

    标签: perl unix solaris shebang


    【解决方案1】:

    shebang 必须是第一行,因为它由 内核 解释,它查看可执行文件开头的两个字节。如果这些是#!,则该行的其余部分被解释为要运行的可执行文件以及该程序可用的脚本文件。 (细节略有不同,但这就是图片)。

    由于内核只会查看前两个字符并且没​​有其他行的概念,因此您必须将 hash bang 放在第 1 行。

    如果内核不能执行以#!whatever 开头的文件会怎样? shell 试图派生一个可执行文件并被内核告知它不能执行该程序,作为最后的手段,它试图将文件内容解释为 shell 脚本。由于 shell 不是 perl,你会得到一堆错误,就像你试图运行一样

     sh temp.pl
    

    【讨论】:

    • 在历史记录中,至少有一种 Unix 风格(忘记是哪一种)使用完整的 4 字符序列 #! /(哈希、砰、空格、斜杠)来检测脚本。
    • 每个人都在说。但这是错误的。命名它,否则它没有发生:-)
    • DYNIX!它在 autoconf“便携式外壳”文档中。但是,该文档似乎在 2009 年进行了更改/更正(我使用 autoconf 的工作是从 1990 年代后期开始的)。 web.archive.org/web/20080118164924/http://www.gnu.org/software/…(所以 id 确实发生了!只是没有再发生了。;-)
    • 据我了解,它只在文档中,但内核代码只看前两个字符,就像任何其他 Unix 内核一样。在被证明是错误的之前,我们可以假设任何#!interpreter 都可以被任何曾经生产过的 Unix 所理解。
    • @Schwern 感谢您提供有关 perl 的信息。尽管如此,直到今天,没有人真正找到exec*() 实现需要空间的内核源文件。虽然我倾向于相信它从未存在过,但有些人认为它存在并灭绝了。在没有化石的情况下,奥卡姆剃刀似乎支持我的立场,不是吗? :-)
    【解决方案2】:

    不只是它必须是第一行,字符#! 必须是文件中的前两个字节。这可以运行脚本是一种 shell 特性,而不是操作系统特性,并且它不特定于任何特定的脚本语言。

    当系统被告知执行文件的内容时,无论是使用 .../path/to/bin/program 之类的东西,还是通过 PATH 的类似路径,它都会检查文件的前几个字节以查找“幻数”它揭示了它是什么类型的文件(您可以使用 file(1) 命令查看该进程)。如果它是已编译的二进制文件,那么它将以适当的方式加载并执行它,如果前两个字节是#!,它将执行“shebang-hack”。

    “shebang-hack”是一些 shell 使用的一种特殊情况(实际上,基本上是每个 shell,但它是约定而不是要求),其中 shell 将剩余字节读取到换行符,解释这些作为文件名,然后执行该文件,将当前文件的其余部分作为输入。加上一些您可能可以阅读的关于 elsewhere 的详细信息。

    一些(版本的)shell 将允许很长的第一行,有些只允许短的;有些允许多个参数,有些只允许一个。

    如果文件不是以#! 开头,但看起来确实是文本,那么一些shell 将试探性地尝试执行它。 Csh(如果我没记错的话)认为它是一个 csh 脚本,如果第一行是空白的,那么对于一些 shell 的行为来说,存在一些复杂而神秘的情况,因为生命太短而无法记住。

    Sven Mascheck's #! page 有很多有趣且广泛的细节(而且是准确的,因为它们与我的回忆相符!)。

    【讨论】:

    • +1 以获得出色而有趣的答案。也许你应该提到这仅适用于类 Unix 系统?
    • 这不是外壳功能,而是操作系统功能。
    • @jlliagre :嗯,是的,也不是。这是一个随着时间的推移从 shell 迁移到内核的功能。目前,像 execve(1) 这样的调用被记录为能够处理这个问题,但从历史上看,是 shell 负责捕捉内核未能将脚本作为二进制文件执行;不同的外壳(sh、csh、ksh、...)的行为也略有不同。因此,在任何特定情况下,魔法实际发生的地方可能微妙地依赖于操作系统和 shell。
    • 承认在上个世纪它有时由贝壳处理。仅当内核不处理 shebang 本身时,默认解释器(AT&T Unix 上的 sh,某些 BSD 上的 csh)才需要。如今,所有 Unix / Unix 类内核都在实现该功能。我相信 ksh 和 bash 不再为 shebang 解析第一行。 zsh 和 perl 正在这样做。无论如何,当脚本是可执行的并且像 sarathi 示例中那样调用时,这种解析是多余的。
    • @jilliagre。它仍然由一些shell处理。例如,某些 Unices 不支持将脚本作为脚本解释器,而某些 shell(如 zsh)仍会退回到自己解释 she-bang 行。
    【解决方案3】:

    除了上面详细介绍的hereherehere 的解释之外,还有一些关于#! 和Perl 的特别之处尚未提及。

    Perl 读取#! 行并做了两件事。首先,如果路径看起来不像 perl,它将使用它重新执行程序!比如……

    #!/bin/sh
    
    echo "Hello world!"
    

    如果以perl /path/to/that/program 执行,将正确运行。我不知道 Perl 这样做的历史原因是什么,但是当您使用 Test::Harness 测试多种语言时,它会派上用场。

    第二件事是 Perl 在#! 行中找到任何开关,并像在命令行上一样应用它们。这就是为什么#!/usr/bin/perl -w 可以打开警告。

    值得一提的是,与 shebang 处理的其他部分不同,这一切都是在 Perl 内部完成的,而不是 Unix,因此可以移植到 Windows。

    Perl + shebang 的另一个注意事项是您可能会在许多 Perl 程序的顶部发现这种疯狂。

    #!/usr/bin/perl
    
    eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
        if 0; # not running under some shell
    

    有时,在非常非常非常古老的系统上,#! 不起作用,Perl 程序由 shell 执行。 eval 强制外壳首先使用 Perl 重新执行文件。由于 shell 语句以换行符结束,它看不到 if 0。 Perl 确实看到了if 0,所以它不执行 eval。 Perl 和 shell 都有语法上等效的 eval 运算符,这使得 hack 起作用。

    【讨论】:

    • 您不应将其他答案引用为 abovebelow
    • 上面是皇家。请将显示器倒置以解决问题。
    • 我认为第一个奇怪行为的原因是,如果你有一个 shebang 不起作用的系统,但你可以为 all 脚本配置一个解释器,你可以将该解释器配置为perl,然后您的系统将像shebang 工作的系统一样运行。 :)
    • 评论必须始终高于 shebang 行。
    【解决方案4】:

    至少在 POSIX 兼容系统上,shebang 用于告诉可执行加载器如何处理设置了可执行位的文本文件。

    加载程序知道如何处理二进制文件,它们以“幻数”开头,现在通常与 ELF 相关。

    另一方面,没有 shebang 的文本文件是由机器上可用的 POSIX 兼容 shell 执行的,这就是为什么会出现这些 shell 错误消息的原因:

    use: Command not found.
    use: Command not found.
    print: Command not found.
    

    当你的可执行文件不被 POSIX 兼容的 shell 解释时,你需要告诉加载器使用什么解释器。其他操作系统(如 Windows)选择文件扩展名来解决这个问题,但 Unix 在这种特定情况下不使用或关心扩展名。它使用的是第一行的shebang,它说明了要使用的命令解释器。唯一的缺点是脚本语言应该忽略第一行。希望是这种情况,因为# 是大多数脚本语言的注释行前缀。

    尽管普遍认为,可移植脚本根本不应该有 shebang。尤其不建议他们使用#!/bin/sh

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-03-07
      • 1970-01-01
      • 2021-04-30
      • 1970-01-01
      • 2011-12-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多