我正在尝试了解shebang的工作原理,并想知道在终端提示符下键入node
时会发生什么。想知道它是否在某个地方的c functions之一中调用main
。我已经使用节点一段时间了,但我只了解基础知识。 shebang #!/usr/bin/env node
以某种方式读取节点可执行文件,我不确定该文件是什么,在哪里,以及它从什么开始。然后是实际计算表达式node
并将其定向到shebang的代码,但这可能太过复杂而无法询问。
答案 0 :(得分:10)
确切地说,“ shebang”只是两个字符#!
。当将Unix系统上的文件作为可执行文件调用(最终通过系统调用execve
)时,内核会查看其前几个字节以确定可执行文件的种类。如果这些字节将其标识为包含机器代码,则内核会将机器代码加载到内存中,并使CPU开始执行它。如果机器代码是从C程序编译的,则其main
函数将最终被调用。 (如果您想了解此过程的工作原理,请阅读John Levine撰写的《链接器和加载器》一书。)
但是,如果前两个字节是#
和!
(ASCII值35和33),则内核将扫描文件的第一行以查找解释器的名称< / em>,然后它将运行解释器,并提供#!
程序的名称作为命令行参数。 (有关内核如何解析第一行的确切详细信息,请参见this answer。)
./foo.js a b c d
并且foo.js以#! /usr/bin/node
开头,然后内核将表现为好像已使用参数vector调用了execve
/usr/bin/node ./foo.js a b c d
,它将打开文件/usr/bin/node
,发现该文件是机器代码可执行文件,然后继续加载机器代码(即Node解释程序)并运行它。然后,Node的main
函数将注意到其第一个参数为./foo.js
,它将打开该文件并将其作为Javascript程序执行,而不是进入其交互式的read-evaluate-print循环。>
Node解释器本身会忽略#!
行,但是它必须在其解析器中包含代码才能忽略它。内核不会将其过滤掉。在Unix上常用的许多解释语言(sh,awk,perl,python,ruby等)中,注释从#
到行尾,因此,此操作自动发生;实际上,之所以选择#!
表示法,是因为 的注释是从#
到行尾。 Javascript注释不能那样工作,因此Node必须在文件开头的#!
有特殊情况。
您显示的#!
行还有一个间接级别:#! /usr/bin/env node
使内核运行带有参数vector的程序/usr/bin/env
(同样由机器代码组成)>
/usr/bin/env node ./foo.js a b c d
env
然后看到它的第一个参数是node
,它沿着搜索路径查找名为node
的程序的可执行文件。搜索路径由环境变量定义:类型
echo $PATH
在您提示时了解它是什么。这是一个用冒号分隔的目录列表。例如,PATH的常用值是
/usr/local/bin:/usr/bin:/bin
表示要按顺序在目录/usr/local/bin
,/usr/bin
和/bin
中查找程序;换句话说,使用PATH的值以及上面的参数,env
首先会尝试运行
/usr/local/bin/node ./foo.js a b c d
,如果没有用,请尝试/usr/bin/node
,依此类推。如果您不知道节点解释器(或任何其他地方)的安装位置,则必须使用此额外的间接寻址,因为内核的#!
处理将仅接受#!
之后的绝对路径名;它不会为您执行PATH搜索。如果您确实知道安装了node
的位置,最好直接编写该路径名,这样程序的行为就不会取决于调用用户的PATH是什么(例如some Linux distributions used to use the name /usr/bin/node
for a completely unrelated program,因此如果#! /usr/bin/env node
,并且用户在PATH上的/opt/node-1.9/bin
之前没有/usr/bin
,就会引起欢笑。
我为execve
和以#!
开头的文件描述的行为是 not specified by POSIX(在该问题上已提到 页,但仅在非标准化RATIONALE部分中)。但是,在当今您可能会遇到的所有类Unix操作系统中,它都是一致的。我不知道到底是几岁。