在脚本中,您必须在第一行包含一个#!
,后面跟着将执行脚本的程序的路径(例如:sh,perl)。#怎么样! shebang的工作?
据我所知,#
字符表示注释的开始,该行应该被执行脚本的程序忽略。看起来,第一行在某些时候被某些东西读取,以便脚本被正确的程序执行。
请问有人可以更多地了解#!
的工作原理吗?
我对此很好奇,所以答案越深入越好。
在脚本中,您必须在第一行包含一个#!
,后面跟着将执行脚本的程序的路径(例如:sh,perl)。#怎么样! shebang的工作?
据我所知,#
字符表示注释的开始,该行应该被执行脚本的程序忽略。看起来,第一行在某些时候被某些东西读取,以便脚本被正确的程序执行。
请问有人可以更多地了解#!
的工作原理吗?
我对此很好奇,所以答案越深入越好。
推荐阅读:
Unix内核的程序加载器是负责做这个。当调用exec()
时,它会要求内核在其参数中从文件加载程序。然后它会检查文件的前16位,以查看它具有的可执行格式。如果它发现这些位是#!
它将使用该文件的第一行的其余部分来查找它应该启动哪个程序,并且它提供了它尝试启动的文件的名称(脚本)作为最后一个参数口译员计划。
解释器然后运行正常,并将#!
作为注释行。
最好的事情是该文件可以是任何东西,不一定是程序 - 只要被调用的程序可以容忍shebang。 – 2014-07-15 13:14:18
@KevinPanko:内核的程序加载器是否总是正好16位?如果'#!'之前是UTF-8或UTF-16 BOM,那么会发生什么? – stakx 2017-01-22 17:17:04
@stakx是的,这是在Unicode之前发明的,从那以后一直没有改变过。 http://unicode.org/faq/utf_bom.html#bom5 – 2017-01-23 21:07:17
简单地说:井号(#!
)线由
读取壳(例如
操作系统的程序加载器。虽然它在形式上看起来像一条评论,但它是文件的前两个字节,将整个文件标记为文本文件和脚本。该脚本将被传递到shebang之后的第一行中提到的可执行文件。瞧!sh
,
bash
等)
略长的故事:想象一下,你有你的脚本,foo.sh
,与可执行位(x
)集。这个文件包含例如以下内容:
#!/bin/sh
# some script commands follow...:
# *snip*
现在,你的shell,键入:
> ./foo.sh
编辑:同时请参阅下列后或您阅读以下之前阅读评论!事实证明,我错了。显然不是将脚本传递给目标解释器的外壳,而是操作系统(内核)本身。
请记住,你的shell进程中输入这个(我们假设这是程序/bin/sh
)。因此,该输入必须由该程序处理。它将该行解释为一个命令,因为它发现该行输入的第一个内容是实际存在的文件的名称,并且该文件的可执行位已设置。
/bin/sh
然后开始读取文件的内容并在文件的最开始处发现shebang(#!
)。对于shell而言,这是一个令牌(“幻数”),通过它可以知道该文件包含脚本。
现在,它如何知道脚本编写的是哪种编程语言呢?毕竟,你可以执行Bash脚本,Perl脚本,Python脚本,......到目前为止,所有的shell都知道它正在查看一个脚本文件(这不是一个二进制文件,而是一个文本文件)。因此它读取下一个输入直到第一个换行符(这将导致/bin/sh
,与上面相比)。这是脚本将通过执行的解释器。 (在这种情况下,目标解释器是shell本身,因此它不必为脚本调用新的shell;它只处理脚本文件本身的其余部分)。
如果脚本是注定的例如Perl解释器会(可选地)必须做的所有事情就是查看shebang行是否真的提到了Perl解释器。否则,Perl解释器会知道它不能执行这个脚本。如果确实在shebang行中提到了Perl解释器,它会读取脚本文件的其余部分并执行它。
Linux内核exec
系统调用使用初始字节#!
来识别文件类型
当您在bash中做:
./something
在Linux上,这要求有充分的exec
系统调用路径something
。
这条线被调用在内核上传递到exec
文件:https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25
如果((bprm-> BUF [0] = '#')||(bprm-> BUF [1 ]!='!'))
它读取文件的第一个字节,并将它们与#!
进行比较。
如果比较结果为真,那么该行的其余部分是由Linux内核,这使得与路径/usr/bin/env python
和当前文件作为第一个参数另一个exec调用解析:
/usr/bin/env python /path/to/script.py
,这适用于任何使用#
作为注释字符的脚本语言。在路径
#!/a
和可执行文件/a
#!
是人类可读的,但是这是没有必要的:
是的,你可以无限循环使用。
如果文件以不同的字节开始,那么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
机制。例如,您可以为.jar
文件添加自定义处理程序:Running a JAR file without directly calling `java`此机制甚至通过文件扩展名支持处理程序。 http://stackoverflow.com/questions/3009192/how-does-the-shebang-work/40938907#40938907
我不认为POSIX指定但是shebangs:https://unix.stackexchange.com/a/346214/32558
我对这个主题的comp.lang.shell线程[可执行程序后记(http://groups.google一个良好的教育。 com/group/comp.unix.shell/browse_thread/thread/e7a3306342c01847/ec5741ed3278408a?q = executable + postscript + programs#ec5741ed3278408a)通过编写一个简单的C程序来操作命令行,我能够为通常不会这样做的语言。 – 2011-10-06 22:36:31