Что именно делает «/ 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интерфейс командной строки по его оригинальному имени без расширения.

mklement0
источник
Можете ли вы дать объяснение или резюме для таких манекенов, как я?
Эндрю Лам
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