Kev*_*nko 42
推荐阅读:
unix内核的程序加载器负责这样做.当exec()
调用它时,它要求内核从其参数的文件加载程序.然后它将检查文件的前16位以查看它具有的可执行格式.如果它发现这些位是#!
它将使用文件第一行的其余部分来查找它应该启动的程序,并且它提供它尝试启动的文件的名称(脚本)作为最后一个参数.口译员计划.
然后解释器正常运行,并将其#!
视为注释行.
简单地说:该家当(#!
)线被读取的外壳(例如操作系统的程序加载器.虽然它正式看起来像一个注释,但它是文件的前两个字节这一事实将整个文件标记为文本文件和脚本.该脚本将被传递给shebang之后的第一行提到的可执行文件.瞧!sh
,bash
等)
稍微长一点的故事:想象一下,你有自己的脚本foo.sh
,并设置了可执行的bit(x
).该文件包含以下内容:
#!/bin/sh
# some script commands follow...:
# *snip*
Run Code Online (Sandbox Code Playgroud)
现在,在shell上输入:
> ./foo.sh
Run Code Online (Sandbox Code Playgroud)
编辑:请在阅读以下内容之后或之前阅读以下评论!事实证明,我错了.显然不是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
Run Code Online (Sandbox Code Playgroud)
在Linux上,这会exec
使用path 调用系统调用./something
。
此行在传递给exec
以下文件的内核中调用:https : //github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
Run Code Online (Sandbox Code Playgroud)
它读取文件的第一个字节,并将其与进行比较#!
。
如果比较结果为真,那么Linux内核将分析其余的行,这将exec
使用路径/usr/bin/env python
和当前文件作为第一个参数进行另一个调用:
/usr/bin/env python /path/to/script.py
Run Code Online (Sandbox Code Playgroud)
这适用于任何#
用作注释字符的脚本语言。
是的,您可以使用以下方法进行无限循环:
printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a
Run Code Online (Sandbox Code Playgroud)
Bash识别错误:
-bash: /a: /a: bad interpreter: Too many levels of symbolic links
Run Code Online (Sandbox Code Playgroud)
#!
是人类可读的,但这不是必需的。
如果文件以不同的字节开头,则exec
系统调用将使用其他处理程序。另一个最重要的内置处理程序用于ELF可执行文件:https : //github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305用于检查字节7f 45 4c 46
(也恰好是人类的)可读.ELF
)。让我们通过读取的前4个字节(/bin/ls
是ELF可执行文件)来确认这一点:
head -c 4 "$(which ls)" | hd
Run Code Online (Sandbox Code Playgroud)
输出:
00000000 7f 45 4c 46 |.ELF|
00000004
Run Code Online (Sandbox Code Playgroud)
因此,当内核看到这些字节时,它将获取ELF文件,将其正确地放入内存,并使用它开始一个新进程。另请参阅:内核如何获取在Linux下运行的可执行二进制文件?
最后,您可以使用该binfmt_misc
机制添加自己的shebang处理程序。例如,您可以为.jar
files添加自定义处理程序。该机制甚至通过文件扩展名支持处理程序。另一个应用程序是使用QEMU透明地运行不同体系结构的可执行文件。
我不认为POSIX会指定shebangs:https ://unix.stackexchange.com/a/346214/32558 ,尽管它在基本原理部分中确实提到,并且形式为“如果系统支持可执行脚本,则可能发生”。macOS和FreeBSD似乎也实现了它。
归档时间: |
|
查看次数: |
8493 次 |
最近记录: |