在终端键入`node`时运行的代码

Lan*_*ard 1 c bash terminal node.js

我正在努力了解shebang是如何工作的,并想知道当你node在终端提示符下输入时会发生什么.想知道它是否main在某处调用某个c函数.我只了解基础知识,已经使用了一段时间的节点.shebang #!/usr/bin/env node以某种方式读取节点可执行文件,我不确定它是什么或在哪里,以及它从什么开始.然后有代码实际上评估表达式node并将其指向shebang,但这可能太复杂而无法询问.

zwo*_*wol 10

令人难以置信的是,"shebang"只是两个角色#!.当Unix系统上的文件作为可执行文件(最终通过系统调用execve)被调用时,内核会查看其前几个字节以确定它是什么类型的可执行文件.如果这些字节将其标识为包含机器代码,则内核会将机器代码加载到内存中并使CPU开始执行它.如果机器代码是从C程序编译的,那么它的main函数最终会被调用.(如果你想知道这个过程是如何运作的,请阅读John Levine的书"Linkers and Loaders".)

但是如果前两个字节是#!(ASCII值35和33)那么内核将扫描文件的第一行以获取解释器的名称,然后它将运行解释器,提供#!程序的名称为命令行参数.(有关如何通过内核解析第一行的详细信息,请参阅此答案.)如果您这样做

./foo.js a b c d
Run Code Online (Sandbox Code Playgroud)

和foo.js一起开始#! /usr/bin/node,然后内核的行为就像execve用参数向量调用一样

/usr/bin/node ./foo.js a b c d
Run Code Online (Sandbox Code Playgroud)

它将打开文件/usr/bin/node,发现这是一个机器代码可执行文件,然后继续加载机器代码,即节点解释器,然后运行它.main然后,Node的函数将注意到它的第一个参数是./foo.js,它将打开该文件并将其作为Javascript程序执行,而不是进入其交互式read-evaluate-print循环.

Node解释器本身忽略了该#!行 - 但它必须在其解析器中有代码才能忽略它; 内核不会过滤掉它.由于Unix上常用的许多解释语言(sh,awk,perl,python,ruby,...)注释从#一行到一行结束,所以这会自动发生; 事实上,这个#!符号是在当天选择的,因为 sh评论是从行#到尾.Javascript注释不起作用,因此Node必须#!在文件开头有一个特殊情况.


#!你展示的那一行有一个额外的间接层:#! /usr/bin/env node让内核运行/usr/bin/env带有参数向量的程序(它再次由机器代码组成)

/usr/bin/env node ./foo.js a b c d
Run Code Online (Sandbox Code Playgroud)

env然后看到它的第一个参数是node,它沿着搜索路径查找名为的程序的可执行文件node.搜索路径由环境变量:type定义

echo $PATH
Run Code Online (Sandbox Code Playgroud)

在你的提示下,了解它是什么.它是以冒号分隔的目录列表.例如,PATH的常见值是

/usr/local/bin:/usr/bin:/bin
Run Code Online (Sandbox Code Playgroud)

这意味着查看目录/usr/local/bin,/usr/bin以及/bin按顺序查看程序; 换句话说,使用PATH的值和上面的参数,env首先会尝试运行

/usr/local/bin/node ./foo.js a b c d
Run Code Online (Sandbox Code Playgroud)

如果这不起作用,它会尝试/usr/bin/node等等.如果您不知道Node解释器(或其他)的安装位置,那么这个额外的间接是必要的,因为内核的#!处理只接受后面的绝对路径名#!; 它不会为您进行PATH搜索.如果您确实知道node安装的位置,最好直接编写该路径名,这样您的程序行为就不依赖于调用用户的PATH是什么(例如,某些Linux发行版使用该名称/usr/bin/node来完成一个完全不相关的程序,所以如果你有#! /usr/bin/env node和用户没有/opt/node-1.9/bin之前/usr/bin对他们的路径,欢闹将随之而来).


该行为我为描述execve与开头的文件#!不是 POSIX指定(它提到该网页上,但只能在非规范基本原理部分).但是,它与您现在可能遇到的所有类Unix操作系统一致.我不知道它到底有多久了.