Was genau macht "/ usr / bin / env node" am Anfang von Knotendateien?

109

Ich hatte diese Zeile #!/usr/bin/env nodeam Anfang einiger Beispiele in gesehen nodejsund gegoogelt, ohne ein Thema zu finden, das den Grund für diese Zeile beantworten könnte.

Die Art der Wörter macht die Suche nicht so einfach.

Ich würde einige lesen javascriptund nodejsBücher vor kurzem , und ich habe mich nicht daran erinnern es in einer von ihnen zu sehen.

Wenn Sie ein Beispiel möchten, können Sie das RabbitMQoffizielle Tutorial sehen , sie haben es in fast allen Beispielen, hier ist eines davon:

#!/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);
});

Könnte mir jemand erklären, was diese Zeile bedeutet?

Was ist der Unterschied, wenn ich diese Zeile setze oder entferne? In welchen Fällen brauche ich es?

Gepser
quelle
3
Grundsätzlich wird die Umgebung der aufrufenden Shell verwendet und diese Umgebung in die angegebene App eingefügt. in diesem Fallnode
Marc B
Eigentlich nein, ich komme nicht von Windows, aber danke, dass Sie Ihre Antwort aktualisiert haben. Ich warte nur darauf, ob jemand mit einer anderen Meinung kommt. Es gibt nur eine Sache, die Sie in Ihrer Antwort nicht erwähnt haben. Ich finde sie erst vor ein paar Stunden. Die Dinge, die sie hier erwähnen, scheinen irgendwie wichtig zu sein, aber es ist mir noch nicht klar genug. stackoverflow.com/questions/14517535/… (Sie können aktualisieren, wenn Sie möchten, ich werde das wirklich schätzen, aber ich fühle es nicht als Verpflichtung, Ihre Antwort ist jetzt gut genug).
Gepser
@ Gepser: Verstanden. Kurz gesagt: Wenn Sie npmein Node.js-Quellenskript als (möglicherweise global verfügbare) CLI installieren möchten , müssen Sie eine Shebang-Zeile verwenden - und npmdies funktioniert sogar unter Windows. siehe meine noch einmal aktualisierte Antwort.
mklement0
"Die Art der Wörter macht die Suche nicht so einfach" - vielleicht möchten Sie duckduckgo.com für diesen speziellen Suchanwendungsfall ausprobieren
Ricardo

Antworten:

146

#!/usr/bin/env nodeist eine Instanz einer Shebang-Zeile : Die allererste Zeile in einer ausführbaren Nur-Text-Datei auf Unix-ähnlichen Plattformen , die dem System über die Befehlszeile nach dem magischen #!Präfix (genannt Shebang ) mitteilt, an welchen Interpreter diese Datei zur Ausführung übergeben werden soll. .

Hinweis: Windows unterstützt keine Shebang-Zeilen , daher werden sie dort effektiv ignoriert . Unter Windows bestimmt nur die Dateinamenerweiterung einer bestimmten Datei , welche ausführbare Datei sie interpretiert. Sie benötigen sie jedoch weiterhin im Kontext vonnpm . [1]

Die folgende allgemeine Diskussion über Shebang-Linien ist auf Unix-ähnliche Plattformen beschränkt:

In der folgenden Diskussion gehe ich davon aus, dass die Datei mit dem Quellcode zur Ausführung durch Node.js einfach benannt ist file.

  • Sie BRAUCHEN diese Zeile , wenn Sie eine Node.js-Quelldatei direkt als eigenständige ausführbare Datei aufrufen möchten. Dies setzt voraus, dass die Datei mit einem Befehl wie "ausführbar" markiert wurde chmod +x ./file, mit dem Sie die Datei aufrufen können mit zum Beispiel ./fileoder, wenn es sich in einem der in der $PATHVariablen aufgelisteten Verzeichnisse befindet , einfach als file.

    • Insbesondere müssen Sie eine Shebang - Zeile erstellen CLIs basierend auf Node.js Quelldateien als Teil eines NPM - Paket , mit dem CLI (e) installiert werden , npmbasierend auf dem Wert des "bin"Schlüssels des in einem Paket - package.jsonDatei ; In dieser Antwort erfahren Sie auch, wie dies mit global installierten Paketen funktioniert . Fußnote [1] zeigt, wie dies unter Windows gehandhabt wird.
  • Sie benötigen diese Zeile NICHT , um eine Datei explizit über den nodeInterpreter aufzurufen , z.node ./file


Optionale Hintergrundinformationen :

#!/usr/bin/env <executableName>ist eine Möglichkeit , einen Interpreter portabel anzugeben: Kurz gesagt: Führen <executableName>Sie ihn aus, wo immer Sie ihn (zuerst) in den in der $PATHVariablen aufgeführten Verzeichnissen finden (und übergeben Sie ihm implizit den Pfad zur vorliegenden Datei).

Dies erklärt die Tatsache, dass ein bestimmter Interpreter plattformübergreifend an verschiedenen Orten installiert werden kann, was bei nodeder Node.js-Binärdatei definitiv der Fall ist.

Dagegen ist die Lage des envkann Dienstprogramm selbst herangezogen werden , in der seine gleichen Stelle über Plattformen, nämlich /usr/bin/env- und die Spezifizierungs vollen zu einem ausführbaren Pfad ist erforderlich , in einer Linie shebang.

Beachten Sie, dass POSIX - Dienstprogramm envwird umgewidmet hier nach Dateinamen zu finden und eine ausführbare Datei in dem ausführen $PATH.
Der eigentliche Zweck von envbesteht darin, die Umgebung für einen Befehl zu verwalten - siehe envdie POSIX-Spezifikation und die hilfreiche Antwort von Keith Thompson .


Es ist auch erwähnenswert , dass Node.js eine Syntax macht Ausnahme für shebang Linien, da sie nicht gültig JavaScript - Code sind ( #ist kein Kommentarzeichen in JavaScript, anders als in POSIX-wie Muscheln und andere Dolmetscher).


[1] Im Interesse der plattformübergreifende Konsistenz, npmerstellt Wrapper *.cmd - Dateien (Batch - Dateien) unter Windows , wenn in einem Paket der angegebenen ausführbare Dateien der Installation package.jsonDatei (über die "bin"Eigenschaft). Im Wesentlichen Dateien diese Wrapper Batch mimischen Unix shebang Funktionalität: Sie rufen Sie die Zieldatei explizit mit der ausführbaren Datei in der Shebang - Zeile angegeben - also Ihre Skripte auch eine Shebang - Zeile enthalten muss , wenn Sie jemals nur beabsichtigen , sie auf Windows laufen - siehe diese Antwort von mir für Details.
Da können *.cmdDateien ohne die aufgerufen werden.cmdDies sorgt für eine nahtlose plattformübergreifende Erfahrung: Unter Windows und Unix können Sie eine npminstallierte CLI effektiv unter ihrem ursprünglichen Namen ohne Erweiterung aufrufen .

mklement0
quelle
Können Sie eine Erklärung oder Zusammenfassung für Dummies wie mich geben?
Andrew Lam
4
@AndrewLam: Unter Windows können Sie Dateinamenerweiterungen wie .cmdund .pyfestlegen, mit welchem ​​Programm solche Dateien ausgeführt werden sollen. Unter Unix führt die Shebang-Zeile diese Funktion aus. Um npmauf allen unterstützten Plattformen arbeiten zu können, benötigen Sie die Shebang-Linie auch unter Windows.
mklement0
28

Skripte, die von einem Interpreter ausgeführt werden sollen, haben normalerweise eine Shebang-Zeile oben, um dem Betriebssystem mitzuteilen, wie sie ausgeführt werden sollen.

Wenn Sie ein Skript mit dem Namen haben, foodessen erste Zeile lautet #!/bin/sh, liest das System diese erste Zeile und führt das Äquivalent von aus /bin/sh foo. Aus diesem Grund sind die meisten Interpreter so eingerichtet, dass sie den Namen einer Skriptdatei als Befehlszeilenargument akzeptieren.

Der Name des Interpreters #!nach muss ein vollständiger Pfad sein. Das Betriebssystem sucht nicht $PATHnach dem Interpreter.

Wenn Sie ein Skript haben, das ausgeführt werden soll node, können Sie die erste Zeile auf folgende Weise schreiben:

#!/usr/bin/node

Das funktioniert aber nicht, wenn der nodeBefehl nicht installiert ist /usr/bin.

Eine übliche Problemumgehung besteht darin, den envBefehl zu verwenden (der nicht wirklich für diesen Zweck vorgesehen war):

#!/usr/bin/env node

Wenn Ihr Skript aufgerufen wird foo, führt das Betriebssystem das Äquivalent von aus

/usr/bin/env node foo

Der envBefehl führt einen anderen Befehl aus, dessen Name in der Befehlszeile angegeben ist, und übergibt alle folgenden Argumente an diesen Befehl. Der Grund, warum es hier verwendet wird, ist, dass envnach $PATHdem Befehl gesucht wird . Wenn nodealso in installiert /usr/local/bin/nodeist und Sie /usr/local/binin Ihrem haben $PATH, wird der envBefehl aufgerufen /usr/local/bin/node foo.

Der Hauptzweck des envBefehls besteht darin, einen anderen Befehl mit einer geänderten Umgebung auszuführen und bestimmte Umgebungsvariablen hinzuzufügen oder zu entfernen, bevor der Befehl ausgeführt wird. Ohne zusätzliche Argumente wird der Befehl jedoch nur in einer unveränderten Umgebung ausgeführt, was in diesem Fall alles ist, was Sie benötigen.

Dieser Ansatz weist einige Nachteile auf. Die meisten modernen Unix-ähnlichen Systeme haben /usr/bin/env, aber ich habe auf älteren Systemen gearbeitet, auf denen der envBefehl in einem anderen Verzeichnis installiert wurde. Möglicherweise gibt es Einschränkungen für zusätzliche Argumente, die Sie mit diesem Mechanismus übergeben können. Wenn der Benutzer nicht das das Verzeichnis enthalten , haben nodeBefehl in $PATH, oder hat einige andere Befehl aufgerufen node, dann könnte es das falsche Kommando oder keine Arbeit überhaupt aufrufen.

Andere Ansätze sind:

  • Verwenden Sie eine #!Zeile, die den vollständigen Pfad zum nodeBefehl selbst angibt , und aktualisieren Sie das Skript nach Bedarf für verschiedene Systeme. oder
  • Rufen Sie den nodeBefehl mit Ihrem Skript als Argument auf.

Siehe auch diese Frage (und meine Antwort ) für weitere Diskussionen über den #!/usr/bin/envTrick.

Auf meinem System (Linux Mint 17.2) ist es übrigens installiert als /usr/bin/nodejs. Nach meinen Notizen, änderte sie sich von /usr/bin/nodezu /usr/bin/nodejszwischen Ubuntu 12.04 und 12.10. Der #!/usr/bin/envTrick hilft dabei nicht (es sei denn, Sie richten einen Symlink oder ähnliches ein).

UPDATE: Ein Kommentar von mtraceur sagt (neu formatiert):

Eine Problemumgehung für das Problem nodejs vs node besteht darin, die Datei mit den folgenden sechs Zeilen zu starten:

#!/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
*/

Dies wird zuerst versuchen nodejsund dann versuchen nodeund die Fehlermeldungen nur drucken, wenn beide nicht gefunden werden. Eine Erklärung liegt außerhalb des Rahmens dieser Kommentare. Ich lasse sie nur hier, falls sie jemandem hilft, mit dem Problem umzugehen, da diese Antwort das Problem angesprochen hat.

Ich habe NodeJS in letzter Zeit nicht verwendet. Ich hoffe, dass das Problem nodejsvs. nodein den Jahren, seit ich diese Antwort zum ersten Mal gepostet habe, behoben wurde. Unter Ubuntu 18.04 wird das nodejsPaket /usr/bin/nodejsals Symlink zu installiert /usr/bin/node. Auf einigen früheren Betriebssystemen (Ubuntu oder Linux Mint, ich bin mir nicht sicher, welches) gab es ein nodejs-legacyPaket, das nodeals Symlink zu bereitgestellt wurde nodejs. Keine Garantie, dass ich alle Details richtig habe.

Keith Thompson
quelle
Eine sehr gründliche Antwort, die das Warum der Dinge liefert.
Suraj Jain
1
Eine Problemumgehung für das nodejsvs- nodeProblem besteht darin, die Datei mit den folgenden sechs Zeilen zu starten: 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) */. Dies wird zuerst versuchen nodejsund dann versuchen nodeund die Fehlermeldungen nur drucken, wenn beide nicht gefunden werden. Eine Erklärung liegt außerhalb des Rahmens dieser Kommentare. Ich lasse sie nur hier, falls sie jemandem hilft, mit dem Problem umzugehen, da diese Antwort das Problem angesprochen hat.
mtraceur
@mtraceur: Ich habe Ihren Kommentar in meine Antwort aufgenommen. Warum die -in der #!Leitung?
Keith Thompson
Das -In #!/bin/sh -ist nur eine Gewohnheit, die sicherstellt, dass sich die Shell unter den extrem engen und unwahrscheinlichen Umständen richtig verhält, unter denen der Skriptname oder der relative Pfad, den die Shell sieht, mit a beginnt -. (Ja, es sieht auch so aus, als ob jede Mainstream-Distribution wieder zusammengewachsen wärenode als als primärer Name . Ich habe nicht nachgeforscht, um meinen Kommentar abzugeben, aber soweit ich weiß, wird nur der Stammbaum der Debian-Distribution verwendet nodejs, und es sieht so aus als ob sie alle wieder zu nodeDebian zurückgekehrt
wären
Technisch bedeutete der einzelne Bindestrich als erstes Argument nicht "Ende der Optionen" - er bedeutete ursprünglich "Ausschalten -xund -v", aber da die frühen Bourne-Likes nur das erste Argument als mögliche Optionen analysierten und die Shell mit diesen Optionen beginnt Es war missbräuchlich, die Shell dazu zu bringen, den Skriptnamen seit dem Original nicht mehr zu analysieren, und bleibt daher missbräuchlich, da das Verhalten aus Kompatibilitätsgründen in modernen Bourne-Likes beibehalten wird. Wenn ich mich an all meine Bourne-Geschichte und Portabilität erinnere, stimmt das.
mtraceur
0

Kurze Antwort: Es ist der Weg zum Dolmetscher.

BEARBEITEN (lange Antwort): Der Grund, warum vor "Knoten" kein Schrägstrich steht, ist, dass Sie die Zuverlässigkeit von #! / Bin / nicht immer garantieren können. Das "/ env" -Bit macht das Programm plattformübergreifender, indem das Skript in einer geänderten Umgebung ausgeführt wird und das Interpreterprogramm zuverlässiger gefunden werden kann.

Sie brauchen es nicht unbedingt, aber es ist gut zu verwenden, um Portabilität (und Professionalität) zu gewährleisten.

Quantum
quelle
1
Das /usr/bin/envBit ändert die Umgebung nicht. Es ist nur ein Befehl an einer (meistens) bekannten Stelle, der einen anderen als Argument angegebenen Befehl aufruft und danach sucht $PATH. Der Punkt ist, dass für die #!Zeile der vollständige Pfad zum aufgerufenen Befehl erforderlich nodeist und Sie nicht unbedingt wissen, wo er installiert ist.
Keith Thompson
Das war es, was ich vorhatte, danke für die Klarstellung!
Quantum