在脚本中,您必须在第一行包含#!
,然后是将执行脚本的程序的路径(例如:sh,perl)。
据我所知,#
字符表示注释的开头,该行应该被执行脚本的程序忽略。看起来,第一行是在某些时候通过某种东西读取,以便脚本由适当的程序执行。
有人可以更多地了解#!
的运作方式吗?
我对此非常好奇,所以答案越深入越好。
推荐阅读:
unix内核的程序加载器负责这样做。调用exec()
时,它会要求内核在其参数中从文件加载程序。然后它将检查文件的前16位以查看它具有的可执行格式。如果它发现这些位是#!
,它将使用文件第一行的其余部分来查找它应该启动的程序,并且它提供它尝试启动的文件的名称(脚本)作为最后一个参数。口译员计划。
然后解释器正常运行,并将#!
视为注释行。
短篇小说:shebang(#!
)系列由读
壳(例如sh
,bash
等)
操作系统的程序加载器。虽然它正式看起来像一个注释,但它是文件的前两个字节这一事实将整个文件标记为文本文件和脚本。该脚本将被传递给shebang之后的第一行提到的可执行文件。瞧!
稍微长一点的故事:想象一下你有你的脚本foo.sh
,可执行位(x
)设置。该文件包含例如下列:
#!/bin/sh
# some script commands follow...:
# *snip*
现在,在shell上输入:
> ./foo.sh
编辑:请在阅读以下内容之后或之前阅读以下评论!事实证明,我错了。显然不是shell将脚本传递给目标解释器,而是操作系统(内核)本身。
请记住,您在shell进程中键入此内容(假设这是程序/bin/sh
)。因此,该输入必须由该程序处理。它将此行解释为命令,因为它发现在该行上输入的第一个内容是实际存在的文件的名称以及具有可执行位的集合。
/bin/sh
然后开始阅读文件的内容并在文件的最开头发现shebang(#!
)。对于shell,这是一个令牌(“幻数”),通过它可以知道该文件包含一个脚本。
现在,它如何知道脚本编写的编程语言呢?毕竟,你可以执行Bash脚本,Perl脚本,Python脚本......所有shell到目前为止都知道它正在查看脚本文件(它不是二进制文件,而是文本文件)。因此,它读取到第一个换行符的下一个输入(这将导致/bin/sh
,与上面的比较)。这是脚本将被传递以执行的解释器。 (在这种特殊情况下,目标解释器是shell本身,因此它不必为脚本调用新的shell;它只是处理脚本文件本身的其余部分。)
如果脚本的目的地是例如/bin/perl
,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的文件的内核中调用此行
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,虽然它确实提到了基本原理部分,并且形式为“如果系统支持可执行脚本可能会发生某些事情”。然而,macOS和FreeBSD似乎也实现了它。