为什么这个shebang线始终是第一线?

我有如下简单的Perl脚本:

#!/usr/bin/perl use strict; use warnings; print "hello ! world\n"; 

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

 >temp.pl hello ! world > 

如果我添加一些这样的评论:

 #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 line都应该始终处于顶端。 但有人可以解释我为什么?

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

由于内核只会查看前两个字符,并且没有更多行的概念,所以您必须将第一行中的哈希放在第一行。

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

  sh < temp.pl 

除了上面的解释之外,在这里和这里以及这里详细介绍了关于#!的一些特别的事情#! 和还没有提到的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强制shell首先用Perl重新执行文件。 由于shell语句以换行结束,因此不会看到if 0 。 Perl会看到if 0 ,所以它不会执行eval。 Perl和shell都有语法上相同的eval操作符,这使得黑客工作。

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

当系统被告知执行一个文件的内容,或者像.../path/to/bin/program ,或者通过PATH的类似路径,它检查文件的前几个字节以寻找这个“魔术数字”揭示了它是什么类型的文件(你可以使用文件(1)命令查看该进程)。 如果它是一个编译的二进制文件,那么它会以适当的方式加载和执行它,如果前两个字节是#! 它会做'shebang-hack'。

“shebang-hack”是一些特殊的情况,实际上,它们都是被shell使用的(事实上,基本上每一个都是约定而不是要求),shell将剩余的字节读到一个换行符,将它们解释为一个文件名,然后执行该文件,将当前文件的其余部分作为输入。 再加上一些细节你可以在其他地方读到。

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

如果该文件不以#!开始#! ,但看起来似乎是文本,有些shell会试探性地试图执行它。 Csh(如果我没有记错的话)把它作为一个csh脚本来处理,而且如果第一行是空白的,那么一些复杂而神秘的情况就会发生。

在Sven Mascheck的#中有很多有趣和广泛的细节(和准确的细节,在这个意义上,它们与我的回忆相匹配!) ! 页面 。

至少在符合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不推荐给他们。