节点文件开头的“ / usr / bin / env节点”到底做什么?


109

#!/usr/bin/env node在一些示例的开头,我已经看到了这一行,nodejs而我在Google上搜索时没有找到任何可以回答该行原因的主题。

单词的性质使搜索变得不那么容易。

我读了一些javascriptnodejs书籍最近,我不记得看到它在任何人。

如果您想要一个示例,则可以查看RabbitMQ官方教程,他们几乎在所有示例中都有它,这里是其中之一:

#!/usr/bin/env node

var amqp = require('amqplib/callback_api');

amqp.connect('amqp://localhost', function(err, conn) {
  conn.createChannel(function(err, ch) {
    var ex = 'logs';
    var msg = process.argv.slice(2).join(' ') || 'Hello World!';

    ch.assertExchange(ex, 'fanout', {durable: false});
    ch.publish(ex, '', new Buffer(msg));
    console.log(" [x] Sent %s", msg);
  });

  setTimeout(function() { conn.close(); process.exit(0) }, 500);
});

有人可以解释一下这行是什么意思吗?

如果放置或删除此行有什么区别?在什么情况下需要?


3
它基本上将调用外壳程序的环境纳入其中,并将该环境填充到指定的任何应用程序中。在这种情况下,node
Marc B

其实不,我不是从Windows来的,但是感谢您更新您的答案。我只是在等待看看是否有人提出了不同的意见。我想您没有在回答中提到一件事,我只是几个小时前才发现。他们在这里提到的事情似乎很重要,但对我来说还不够清楚。stackoverflow.com/questions/14517535/…(您可以根据需要进行更新,我会很感激,但是不觉得这是一种义务,您的回答现在已经足够好了)。
Gepser

@Gepser:知道了。简而言之就是:如果您想npm将Node.js源脚本安装为CLI(可能在全球范围内使用),则必须使用shebang行- npm甚至可以在Windows上运行;再次查看我的最新答案。
mklement0 2015年

“单词的性质使搜索变得不那么容易” –您可能需要尝试使用duckduckgo.com来查找特定的搜索用例
里卡多(Ricardo),

Answers:


146

#!/usr/bin/env nodeshebang行的一个实例类Unix平台上的可执行纯文本文件中的第一行,它告诉系统哪个解释器通过魔术#!前缀(称为shebang)后的命令行将该文件传递给执行解释器。 。

注:的Windows没有支持家当线,所以他们有效地忽略了那里。在Windows上,它只是给定文件的文件扩展名,它确定哪个可执行文件将对其进行解释。但是,您仍然需要在中使用它们npm[1]

下面对shebang行的一般讨论仅限于类Unix平台:

在下面的讨论中,我将假定包含要由Node.js执行的源代码的文件简称为file

  • 需要此行,如果你要调用一个Node.js的源文件,直接,因为它本身就是一个可执行文件-这是假定该文件已被标记为可执行的命令,例如chmod +x ./file,然后可以让您调用文件例如,带有,./file或者,如果它位于$PATH变量中列出的目录之一中,则简称为file

    • 具体来说,您需要一个shebang行,以作为npm 软件包的一部分基于Node.js源文件创建CLI,并根据软件包文件中密钥的值来安装CLI ;另请参阅此答案以了解如何与全局安装的软件包一起使用。脚注[1]显示了如何在Windows上进行处理。npm"bin"package.json
  • 不需要此行即可通过node解释器显式调用文件,例如,node ./file


可选背景信息

#!/usr/bin/env <executableName>是一种可移植地指定解释器的方法:简而言之,它说:<executableName>$PATH变量中列出的目录中的任何位置(首先)找到它(然后将其隐式传递到当前文件)都执行。

这说明了给定的解释器可能安装在跨平台的不同位置的事实node,对于Node.js二进制文件绝对是这种情况。

相比之下,env实用程序本身的位置可以依赖于跨平台的相同位置,即/usr/bin/env-并且在shebang行中需要指定可执行文件的完整路径。

需要注意的是POSIX工具env重新利用这里的文件名来查找,并在执行可执行文件$PATH
的真正目的env是管理命令的环境-请参阅envPOSIX规范Keith Thompson的有用答案


还值得注意的是,Node.js正在对shebang行进行语法例外,因为它们不是有效的JavaScript代码(#与POSIX一样的shell和其他解释器不同,它不是JavaScript中的注释字符)。


[1]为了跨平台的一致性,在安装包文件中指定的可执行文件时(通过属性),在Windows上npm创建包装器 *.cmd文件(批处理文件)。本质上,这些包装批处理文件模仿 Unix shebang功能:它们使用shebang行中指定的可执行文件显式调用目标文件 -因此,即使您只打算在Windows上运行它们您的脚本也必须包含shebang行 -查看此答案我的细节。 由于可以在不使用文件的情况下调用文件package.json"bin"
*.cmd.cmd扩展名,这提供了无缝的跨平台体验:在Windows和Unix上,您都可以npm使用其原始的无扩展名有效地调用安装的CLI。


您能为像我这样的假人提供解释或摘要吗?
安德鲁·林

4
@AndrewLam:在Windows上,文件扩展名例如.cmd.py确定将使用什么程序来执行这些文件。在Unix上,shebang行执行该功能。要npm在所有支持的平台上工作,即使在Windows上也需要shebang行。
mklement0

28

解释器要执行的脚本通常在顶部有一个shebang行,以告诉OS如何执行它们。

如果您有一个脚本foo的第一行名为#!/bin/sh,则系统将读取该第一行并执行与之等效的脚本/bin/sh foo。因此,大多数解释器都设置为接受脚本文件的名称作为命令行参数。

后面的解释器名称#!必须是完整路径;操作系统将不会搜索您$PATH找到解释器。

如果您有一个脚本要由执行node,则写第一行的明显方法是:

#!/usr/bin/node

但是如果node未在中安装命令,则无法使用/usr/bin

一个常见的解决方法是使用env命令(这是没有真正用于这一目的):

#!/usr/bin/env node

如果您的脚本被调用foo,则操作系统将执行以下操作:

/usr/bin/env node foo

env命令执行另一个命令,该命令的名称在其命令行上给出,并将以下所有参数传递给该命令。在这里使用它的原因是env将搜索$PATH该命令。因此,如果node安装在/usr/local/bin/node,你有/usr/local/bin你的$PATH,该env命令将调用/usr/local/bin/node foo

env命令的主要目的是在修改后的环境下执行另一个命令,在运行该命令之前添加或删除指定的环境变量。但是没有其他参数,它只是在不改变环境的情况下执行命令,这是您在此情况下所需要的。

这种方法有一些缺点。大多数现代的类Unix系统都有/usr/bin/env,但是我在较旧的系统上工作,该env命令安装在不同的目录中。使用此机制可以传递的其他参数可能有所限制。如果用户在目录中没有包含该node命令的目录$PATH,或者没有node调用另一个不同的命令,则它可能会调用错误的命令或根本无法工作。

其他方法是:

  • 使用#!一行指定node命令本身的完整路径,并根据不同系统的需要更新脚本;要么
  • node以您的脚本作为参数调用命令。

另请参见此问题(以及我的回答),以获取有关该#!/usr/bin/env技巧的更多讨论。

顺便说一句,在我的系统(Linux Mint 17.2)上,它安装为/usr/bin/nodejs。根据我的笔记,它从Ubuntu 12.04到12.10之间更改/usr/bin/node/usr/bin/nodejs。这个#!/usr/bin/env技巧将无济于事(除非您设置了符号链接或类似的东西)。

更新:mtraceur的评论说(重新格式化):

nodejs vs node问题的解决方法是使用以下六行启动文件:

#!/bin/sh -
':' /*-
test1=$(nodejs --version 2>&1) && exec nodejs "$0" "$@"
test2=$(node --version 2>&1) && exec node "$0" "$@"
exec printf '%s\n' "$test1" "$test2" 1>&2
*/

这将首先尝试nodejs,然后再尝试node,并且仅在找不到两个错误消息的情况下才打印错误消息。这些注释超出了解释范围,我将其保留在此处,以防它帮助任何人解决问题,因为此答案引起了问题。

我最近没有使用过NodeJS。我希望自从我首次发布此答案以来的几年中,nodejsvs。node问题已经解决。在Ubuntu 18.04上,该nodejs软件包/usr/bin/nodejs以符号链接的形式安装到/usr/bin/node。在某些较早的操作系统(Ubuntu或Linux Mint,我不确定是哪一个)上,有一个nodejs-legacy软件包node作为与的符号链接提供nodejs。不保证我拥有所有详细信息。


提供问题原因的非常彻底的答案。
Suraj Jain

1
nodejsvs node问题的解决方法是使用以下六行启动文件:1)#!/bin/sh -,2)':' /*-3)test1=$(nodejs --version 2>&1) && exec nodejs "$0" "$@"4)test2=$(node --version 2>&1) && exec node "$0" "$@",5)exec printf '%s\n' "$test1" "$test2" 1>&26)*/。这将首先尝试nodejs,然后再尝试node,并且仅在找不到两个错误消息的情况下才打印错误消息。这些注释超出了解释范围,我将其保留在此处,以防它帮助任何人解决问题,因为此答案引起了问题。
mtraceur

@mtraceur:我已将您的评论纳入我的答案。为什么要-#!线?
基思·汤普森

-#!/bin/sh -仅仅是一个习惯,确保外壳的行为就在极窄,不可能集的情况下,该脚本的名称或相对路径的外壳看到开始有-。(此外,是的,看起来每个主流发行版都已汇聚node为主要名称。我在发表评论时并没有认真检查,但据我所知,仅使用了Debian发行版家族树nodejs,并且看起来就像他们全都恢复了nodeDebian所做的一样。)
mtraceur

从技术上讲,作为第一个参数的破折号并不意味着“选项的结束”,它最初的意思是“关闭-x-v”,但是由于早期的伯恩式风格仅将第一个参数解析为可能的选项,并且由于shell以关闭这些选项开头,可导致外壳程序不尝试从原始名称开始解析脚本名称,并且由于基于兼容性的原因在现代Bourne-like中保持该行为,因此仍然可滥用。如果我还记得我所有的伯恩历史和可移植性琐事。
mtraceur

0

简短答案:这是到达解释器的路径。

编辑(长答案):之所以在“节点”之前没有斜杠是因为您不能总是保证#!/ bin /的可靠性。通过在修改后的环境中运行脚本,“ / env”位使程序更具跨平台性,并且更可靠地能够找到解释器程序。

您不一定需要它,但是最好使用它来确保可移植性(和专业性)


1
/usr/bin/env位不会改变环境。它只是一个(通常)已知位置的命令,它调用另一个作为参数给出的命令,并搜索$PATH以找到它。关键是该#!行需要被调用命令的完整路径,并且您不一定知道node安装在哪里。
基思·汤普森

那就是我要去的地方,谢谢您的澄清!
量子
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.