Что именно делает «/ usr / bin / env node» в начале файлов node?


110

Я видел эту строку #!/usr/bin/env nodeв начале некоторых примеров nodejsи искал в Google, не найдя темы, которая могла бы ответить на причину этой строки.

Природа слов затрудняет поиск.

Я прочитал некоторые javascriptи nodejsкниги в последнее время, и я не помню , что видел его в любом из них.

Если вам нужен пример, вы можете увидеть 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 в качестве (потенциально доступного глобально) интерфейса командной строки , вы должны использовать строку shebang - и npmдаже заставить ее работать в Windows; см. мой еще раз обновленный ответ.
mklement0 04

«Природа слов делает поиск не таким простым» - вы можете попробовать duckduckgo.com для этого конкретного варианта использования поиска
Рикардо

Возможный дубликат Почему люди пишут #! / Usr / bin / env python shebang в первой строке сценария Python? . Тот же вопрос, другой переводчик.
jww

Ответы:


146

#!/usr/bin/env nodeявляется экземпляром строки shebang : самая первая строка в исполняемом текстовом файле на Unix-подобных платформах, которая сообщает системе, какой интерпретатор передать этот файл для выполнения , через командную строку, следующую за магическим #!префиксом (называемым shebang ) .

Примечание: Windows , никак не поддерживает притон линии , поэтому они эффективно игнорируются там; в Windows только расширение имени файла данного файла определяет, какой исполняемый файл будет его интерпретировать. Однако они все равно нужны вам в контекстеnpm . [1]

Следующее общее обсуждение строк shebang ограничено Unix-подобными платформами:

В следующем обсуждении я предполагаю, что файл, содержащий исходный код для выполнения Node.js, имеет просто имя file.

  • Вам НУЖНА эта строка , если вы хотите напрямую вызвать исходный файл Node.js как самостоятельный исполняемый файл - это предполагает, что файл был помечен как исполняемый с помощью такой команды, как chmod +x ./file, которая затем позволяет вам вызывать файл с, например,, ./fileили, если он находится в одном из каталогов, перечисленных в $PATHпеременной, просто как file.

    • В частности, вам нужна строка shebang для создания интерфейсов командной строки на основе исходных файлов Node.js как части пакета npm , при этом интерфейсы командной строки, устанавливаемые на npmоснове значения "bin"ключа в package.jsonфайле пакета ; также посмотрите этот ответ, чтобы узнать, как это работает с глобально установленными пакетами. В сноске [1] показано, как это делается в Windows.
  • Эта строка НЕ нужна для явного вызова файла через nodeинтерпретатор, например,node ./file


Дополнительная справочная информация :

#!/usr/bin/env <executableName>- это способ переносимого определения интерпретатора: в двух словах он гласит: выполнять <executableName>везде, где вы (сначала) найдете его среди каталогов, перечисленных в $PATHпеременной (и неявно передать ему путь к файлу под рукой).

Это объясняет тот факт, что данный интерпретатор может быть установлен в разных местах на разных платформах, что, безусловно, относится nodeк двоичному файлу Node.js.

Напротив, envможно полагаться , что расположение самой утилиты будет в одном и том же месте на разных платформах, а именно /usr/bin/env- и требуется указать полный путь к исполняемому файлу в строке shebang.

Обратите внимание , что POSIX утилита envв настоящее время переориентирована здесь , чтобы найти по имени файла и выполнить исполняемый в $PATH.
Истинная цель env- управлять средой для команды - см. Спецификацию envPOSIX и полезный ответ Кейта Томпсона .


Также стоит отметить, что Node.js делает синтаксическое исключение для строк shebang, учитывая, что они не являются допустимым кодом JavaScript ( #не является символом комментария в JavaScript, в отличие от POSIX-подобных оболочек и других интерпретаторов).


[1] В интересах кросс-платформенной согласованности npmсоздает файлы- оболочки *.cmd (командные файлы) в Windows при установке исполняемых файлов, указанных в package.jsonфайле пакета (через "bin"свойство). По сути, эти пакетные файлы оболочки имитируют функциональность Unix shebang: они явно вызывают целевой файл с исполняемым файлом, указанным в строке shebang - таким образом, ваши сценарии должны включать строку shebang, даже если вы собираетесь запускать их только в Windows - см. Этот ответ для подробностей.
Поскольку *.cmdфайлы можно вызывать без.cmdрасширение, это обеспечивает беспроблемный кроссплатформенный опыт: как в Windows, так и в Unix вы можете эффективно вызывать установленный npmинтерфейс командной строки по его оригинальному имени без расширения.


Можете ли вы дать объяснение или резюме для таких манекенов, как я?
Эндрю Лам

4
@AndrewLam: в Windows расширения имен файлов, такие как .cmdи, .pyопределяют, какая программа будет использоваться для выполнения таких файлов. В Unix эту функцию выполняет строка shebang. Чтобы npmработать на всех поддерживаемых платформах, вам понадобится строка shebang даже в Windows.
mklement0

28

Сценарии, которые должны выполняться интерпретатором, обычно имеют строку shebang вверху, чтобы сообщить ОС, как их выполнять.

Если у вас есть сценарий с именем 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. По моим записям, он изменился от /usr/bin/nodeдо /usr/bin/nodejsмежду Ubuntu 12.04 и 12.10. #!/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. Нет гарантии, что у меня есть все подробности.


Очень подробный ответ, объясняющий, почему.
Сурадж Джайн

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, и это выглядит как будто все они вернулись к поддержке, как nodeтолько это сделал Debian.)
mtraceur

Технически одиночное тире в качестве первого аргумента не означало «конец параметров» - оно первоначально означало «выключить -xи -v», но поскольку ранние борн-лайки анализировали только первый аргумент как возможные варианты, и поскольку оболочка запускается с отключенными этими параметрами , было злоупотреблением заставлять оболочку не пытаться анализировать имя сценария, начиная с оригинала, и, таким образом, остается объектом злоупотреблений, поскольку поведение поддерживается в современных борн-лайках по соображениям совместимости. Если я правильно помню всю свою историю Борна и мелочи по переносимости.
mtraceur

0

Краткий ответ: это путь к интерпретатору.

РЕДАКТИРОВАТЬ (длинный ответ): Причина, по которой перед «узлом» нет косой черты, заключается в том, что вы не всегда можете гарантировать надежность #! / Bin /. Бит «/ env» делает программу более кроссплатформенной за счет запуска сценария в измененной среде и более надежного поиска программы-интерпретатора.

Вам это не обязательно нужно, но его можно использовать для обеспечения портативности (и профессионализма)


1
/usr/bin/envБит не изменяет окружающую среду. Это просто команда в (в основном) известном месте, которая вызывает другую команду, заданную в качестве аргумента, и ищет $PATHее. Дело в том, что в #!строке требуется полный путь к вызываемой команде, и вы не обязательно знаете, где nodeона установлена.
Кейт Томпсон

Это было то, к чему я стремился, спасибо за разъяснения!
Quantum
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.