Che cosa fa esattamente "/ usr / bin / env node" all'inizio dei file del nodo?


109

Avevo visto questa riga #!/usr/bin/env nodeall'inizio di alcuni esempi in nodejse avevo cercato su Google senza trovare alcun argomento che potesse rispondere al motivo di quella riga.

La natura delle parole rende la ricerca non così facile.

Ne avevo letti alcuni javascripte nodejslibri di recente e non ricordavo di averlo visto in nessuno di essi.

Se vuoi un esempio, potresti vedere il tutorialRabbitMQ ufficiale , ce l'hanno in quasi tutti i loro esempi, eccone uno:

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

Qualcuno potrebbe spiegarmi qual è il significato di questa riga?

Qual è la differenza se inserisco o rimuovo questa riga? In quali casi ne ho bisogno?


3
fondamentalmente prende l'ambiente della shell chiamante e inserisce quell'ambiente in qualsiasi app specificata. in questo caso,node
Marc B

In realtà no, non vengo da Windows, ma grazie per aver aggiornato la tua risposta. Sto solo aspettando di vedere se qualcun altro arriva con un'opinione diversa. C'è solo una cosa che penso tu non abbia menzionato nella tua risposta, l'ho trovata solo un paio d'ore fa. Le cose che menzionano qui sembrano importanti, ma non sono ancora abbastanza chiare per me. stackoverflow.com/questions/14517535/… (puoi aggiornare se vuoi, lo apprezzerò davvero, ma non sentirlo come un obbligo, la tua risposta è abbastanza buona in questo momento).
Gepser

@Gepser: capito. In breve: se si desidera npminstallare uno script sorgente Node.js come una CLI (potenzialmente disponibile a livello globale) , è necessario utilizzare una riga shebang - e npmfunzionerà anche su Windows; vedere la mia risposta ancora una volta aggiornata.
mklement0

"La natura delle parole rende la ricerca non così facile" - potresti provare duckduckgo.com per questo specifico caso di utilizzo della ricerca
Ricardo

Risposte:


146

#!/usr/bin/env nodeè un'istanza di una riga shebang : la primissima riga in un file di testo semplice eseguibile su piattaforme tipo Unix che dice al sistema a quale interprete passare quel file per l'esecuzione , tramite la riga di comando che segue il #!prefisso magico (chiamato shebang ) .

Nota: di Windows non non supporta le linee Shebang , quindi sono effettivamente ignorato là; su Windows è solo l' estensione del nome file di un dato file che determina quale eseguibile lo interpreterà. Tuttavia, ne hai ancora bisogno nel contesto dinpm . [1]

La seguente discussione generale sulle linee di shebang è limitata alle piattaforme simili a Unix:

Nella discussione seguente assumerò che il file contenente il codice sorgente per l'esecuzione da parte di Node.js sia semplicemente denominato file.

  • È BISOGNO questa linea , se si desidera richiamare un file sorgente Node.js direttamente , come un eseguibile a sé stante - questo presuppone che il file è stato contrassegnato come eseguibile con un comando come chmod +x ./file, che poi ti permette di richiamare il file con, ad esempio, ./fileo, se si trova in una delle directory elencate nella $PATHvariabile, semplicemente come file.

    • In particolare, è necessaria una riga shebang per creare CLI basati sui file sorgente Node.js come parte di un pacchetto npm , con le CLI da installare in npmbase al valore della "bin"chiave nel package.jsonfile di un pacchetto ; vedi anche questa risposta per come funziona con i pacchetti installati a livello globale . La nota [1] mostra come questo viene gestito su Windows.
  • NON hai bisogno di questa riga per invocare un file esplicitamente tramite l' nodeinterprete, ad esempio,node ./file


Informazioni di base facoltative :

#!/usr/bin/env <executableName>è un modo per specificare in modo portabile un interprete: in poche parole, dice: esegui <executableName>ovunque lo (prima) lo trovi tra le directory elencate nella $PATHvariabile (e gli passi implicitamente il percorso del file in questione).

Ciò spiega il fatto che un determinato interprete può essere installato in posizioni diverse su piattaforme, il che è sicuramente il caso nodedel binario Node.js.

Al contrario, envsi può fare affidamento sulla posizione dell'utilità stessa nella stessa posizione su tutte le piattaforme, vale a dire /usr/bin/env- e la specifica del percorso completo di un eseguibile è richiesta in una riga shebang.

Si noti che l'utilità POSIX envviene riproposta qui per individuare in base al nome del file ed eseguire un eseguibile in $PATH.
Il vero scopo di envè gestire l'ambiente per un comando - vedere envle specifiche POSIX di e l'utile risposta di Keith Thompson .


Vale anche la pena notare che Node.js sta facendo un'eccezione di sintassi per le righe shebang, dato che non sono codice JavaScript valido ( #non è un carattere di commento in JavaScript, a differenza delle shell POSIX e di altri interpreti).


[1] Nell'interesse della coerenza multipiattaforma, npmcrea file wrapper *.cmd (file batch) su Windows durante l'installazione di eseguibili specificati nel file di un pacchetto package.json(tramite la "bin"proprietà). In sostanza, questi file batch wrapper imitano la funzionalità shebang di Unix: invocano il file di destinazione esplicitamente con l'eseguibile specificato nella riga shebang - quindi, i tuoi script devono includere una riga shebang anche se intendi eseguirli solo su Windows - vedi questa risposta mio per i dettagli.
Poiché i *.cmdfile possono essere richiamati senza l'estensione.cmdestensione, questo consente un'esperienza multipiattaforma senza interruzioni: sia su Windows che su Unix è possibile richiamare efficacemente una npmCLI installata con il suo nome originale senza estensione.


Potete fornire una spiegazione o un riepilogo per i manichini come me?
Andrew Lam

4
@AndrewLam: su Windows, le estensioni dei nomi dei file come .cmde .pydeterminano quale programma verrà utilizzato per eseguire tali file. Su Unix, la riga shebang esegue quella funzione. Per npmlavorare su tutte le piattaforme supportate, è necessaria la linea shebang anche su Windows.
mklement0

28

Gli script che devono essere eseguiti da un interprete normalmente hanno una linea shebang in alto per dire al sistema operativo come eseguirli.

Se hai uno script chiamato la foocui prima riga è #!/bin/sh, il sistema leggerà quella prima riga ed eseguirà l'equivalente di /bin/sh foo. Per questo motivo, la maggior parte degli interpreti è configurata per accettare il nome di un file di script come argomento della riga di comando.

Il nome dell'interprete che segue #!deve essere un percorso completo; il sistema operativo non cercherà il tuo $PATHper trovare l'interprete.

Se hai uno script da eseguire node, il modo più ovvio per scrivere la prima riga è:

#!/usr/bin/node

ma non funziona se il nodecomando non è installato in /usr/bin.

Una soluzione alternativa comune consiste nell'usare il envcomando (che non era realmente destinato a questo scopo):

#!/usr/bin/env node

Se lo script viene chiamato foo, il sistema operativo eseguirà l'equivalente di

/usr/bin/env node foo

Il envcomando esegue un altro comando il cui nome è dato sulla sua riga di comando, passando i seguenti argomenti a quel comando. Il motivo per cui viene utilizzato qui è che envcercherà $PATHil comando. Quindi, se nodeè installato in /usr/local/bin/node, e lo hai /usr/local/binnel tuo $PATH, il envcomando verrà richiamato /usr/local/bin/node foo.

Lo scopo principale del envcomando è eseguire un altro comando con un ambiente modificato, aggiungendo o rimuovendo le variabili di ambiente specificate prima di eseguire il comando. Ma senza argomenti aggiuntivi, esegue semplicemente il comando con un ambiente invariato, che è tutto ciò di cui hai bisogno in questo caso.

Ci sono alcuni svantaggi in questo approccio. La maggior parte dei moderni sistemi Unix lo hanno /usr/bin/env, ma ho lavorato su sistemi più vecchi in cui il envcomando era installato in una directory diversa. Potrebbero esserci limitazioni su argomenti aggiuntivi che puoi passare usando questo meccanismo. Se l'utente non ha la directory contenente il nodecomando $PATHo ha chiamato un comando diverso node, potrebbe richiamare il comando sbagliato o non funzionare affatto.

Altri approcci sono:

  • Utilizzare una #!riga che specifica il percorso completo del nodecomando stesso, aggiornando lo script secondo necessità per i diversi sistemi; o
  • Invoca il nodecomando con il tuo script come argomento.

Vedi anche questa domanda (e la mia risposta ) per ulteriori discussioni sul #!/usr/bin/envtrucco.

Per inciso, sul mio sistema (Linux Mint 17.2), è installato come /usr/bin/nodejs. Secondo le mie note, è cambiato da /usr/bin/nodea /usr/bin/nodejstra Ubuntu 12.04 e 12.10. Il #!/usr/bin/envtrucco non aiuta in questo (a meno che tu non abbia impostato un collegamento simbolico o qualcosa di simile).

AGGIORNAMENTO: Un commento di mtraceur dice (riformattato):

Una soluzione alternativa per il problema nodejs vs node è avviare il file con le seguenti sei righe:

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

Questo proverà prima nodejse poi proverà nodee stamperà i messaggi di errore solo se non vengono trovati entrambi. Una spiegazione esula dallo scopo di questi commenti, la lascio qui nel caso in cui aiuti qualcuno ad affrontare il problema poiché questa risposta ha sollevato il problema.

Non ho usato NodeJS ultimamente. La mia speranza è che il problema nodejsvs. nodesia stato risolto negli anni trascorsi da quando ho pubblicato questa risposta per la prima volta. Su Ubuntu 18.04, il nodejspacchetto viene installato /usr/bin/nodejscome collegamento simbolico a /usr/bin/node. Su alcuni sistemi operativi precedenti (Ubuntu o Linux Mint, non sono sicuro di quale), c'era un nodejs-legacypacchetto che forniva nodeun collegamento simbolico a nodejs. Nessuna garanzia di avere tutti i dettagli corretti.


Una risposta molto completa che fornisce il perché delle cose.
Suraj Jain

1
Una soluzione alternativa per il problema nodejsvs nodeè avviare il file con le seguenti sei righe: 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) */. Questo proverà prima nodejse poi proverà nodee stamperà i messaggi di errore solo se non vengono trovati entrambi. Una spiegazione esula dallo scopo di questi commenti, la lascio qui nel caso in cui aiuti qualcuno ad affrontare il problema poiché questa risposta ha sollevato il problema.
mtraceur

@mtraceur: ho incorporato il tuo commento nella mia risposta. Perché -in #!linea?
Keith Thompson

L' -in #!/bin/sh -è solo un'abitudine che consente di verificare le si comporta shell proprio nel molto stretta ed è improbabile insieme di circostanze che il nome dello script o relativo percorso che vede il guscio comincia con una -. (Inoltre, sì, sembra che ogni distribuzione mainstream sia tornata nodecome nome principale. Non sono andato a scavare per controllare quando ho fatto il mio commento, ma per quanto ne so è stato utilizzato solo l'albero genealogico della distribuzione Debian nodejs, e sembra come se fossero tornati tutti a supportare anche nodeuna volta Debian.)
mtraceur

Tecnicamente il trattino singolo come primo argomento non significava "fine delle opzioni" - originariamente significava "spegni -xe -v", ma poiché i primi Bourne-like analizzavano solo il primo argomento come possibili opzioni, e poiché la shell inizia con quelle opzioni disattivate , era abusabile fare in modo che la shell non tentasse di analizzare il nome dello script dall'originale, e rimane quindi abusabile perché il comportamento è mantenuto nei moderni Bourne-like per motivi di compatibilità. Se ricordo bene tutta la mia storia di Bourne e le mie curiosità sulla portabilità.
mtraceur

0

Risposta breve: è il percorso dell'interprete.

EDIT (Long Answer): Il motivo per cui non c'è barra prima di "node" è perché non puoi sempre garantire l'affidabilità di #! / Bin /. Il bit "/ env" rende il programma più multipiattaforma eseguendo lo script in un ambiente modificato e riuscendo a trovare il programma interprete in modo più affidabile.

Non serve necessariamente, ma è bene utilizzarlo per garantire portabilità (e professionalità)


1
Il /usr/bin/envbit non modifica l'ambiente. È solo un comando in una posizione (per lo più) nota che richiama un altro comando dato come argomento e cerca $PATHdi trovarlo. Il punto è che la #!riga richiede il percorso completo del comando che viene richiamato e non si sa necessariamente dove nodesia installato.
Keith Thompson

Questo era quello che stavo facendo, grazie per aver chiarito!
Quantum
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.