Qual è la differenza tra “#! / Usr / bin / env bash” e “#! / Usr / bin / bash”?


372

Nell'intestazione di uno script Bash, qual è la differenza tra queste due affermazioni:

  1. #!/usr/bin/env bash

  2. #!/usr/bin/bash

Quando ho consultato la env pagina man , ho ottenuto questa definizione:

 env - run a program in a modified environment

Cosa significa?



6
Chi può dirmi perché questa domanda è chiusa, "correlata alla programmazione o allo sviluppo del software" non lo è?
Tarrsalah,

1
Sono d'accordo che non è off-topic, ma è probabilmente un duplicato di molte altre domande come questa .
Keith Thompson,

16
Questa domanda non avrebbe dovuto essere contrassegnata come fuori tema. Ha solo bisogno di 5 persone con un punteggio superiore a 3000 per contrassegnarlo come "sull'argomento" e può essere riaperto. È una domanda, in particolare sulla programmazione.
Danijel-James W,

3
Sono scioccato. Scioccato di scoprire che la documentazione di Linux è piena di tautologie. xkcd.com/703 git-man-page-generator.lokaltog.net
allyourcode

Risposte:


323

L'esecuzione di un comando attraverso /usr/bin/envha il vantaggio di ricerca di tutto ciò che la versione di default del programma è nel tuo attuale ENV ironment.

In questo modo, non è necessario cercarlo in una posizione specifica sul sistema, poiché tali percorsi potrebbero trovarsi in posizioni diverse su sistemi diversi. Finché è sul tuo cammino, lo troverà.

Un aspetto negativo è che non sarai in grado di passare più di un argomento (ad esempio non sarai in grado di scrivere /usr/bin/env awk -f) se desideri supportare Linux, poiché POSIX è vago su come interpretare la linea e Linux interpreta tutto dopo il primo spazio per indicare un singolo argomento. Puoi usarlo /usr/bin/env -Ssu alcune versioni envper aggirare questo, ma poi lo script diventerà ancora meno portatile e si romperà su sistemi abbastanza recenti (ad esempio anche Ubuntu 16.04 se non in seguito).

Un altro aspetto negativo è che poiché non si chiama un eseguibile esplicito, ha il potenziale per errori e per problemi di sicurezza dei sistemi multiutente (se qualcuno è riuscito a ottenere il proprio eseguibile chiamato bashnel tuo percorso, ad esempio).

#!/usr/bin/env bash #lends you some flexibility on different systems
#!/usr/bin/bash     #gives you explicit control on a given system of what executable is called

In alcune situazioni, la prima può essere preferita (come l'esecuzione di script Python con più versioni di Python, senza dover rielaborare la riga eseguibile). Ma in situazioni in cui la sicurezza è al centro dell'attenzione, sarebbe preferibile quest'ultima, poiché limita le possibilità di immissione del codice.


22
Un altro svantaggio è che non è possibile passare un argomento aggiuntivo all'interprete.
Keith Thompson,

1
@KeithThompson: informazioni errate. Puoi passare le opzioni all'interprete sottostante usando / usr / bin / env!
Gaurav Agarwal,

4
@GauravAgarwal: non sul mio sistema. Uno script che contiene solo questa singola riga: #!/usr/bin/env echo Hellosi lamenta: /usr/bin/env: echo Hello: No such file or directory. Apparentemente tratta echo Hellocome un unico argomento /usr/bin/env.
Keith Thompson,

1
@ AndréLaszlo: il envcomando certamente consente di passare argomenti al comando. Il problema è la semantica della #!linea, e dipende dal kernel. I kernel Linux recenti consentono cose del genere #!/usr/bin/env command args, ma i kernel Linux più vecchi e altri sistemi no.
Keith Thompson,

1
Perché ci sono backtick nel blocco di codice? Non dovrebbero essere rimossi?
Benjamin W.

64

L'uso di #!/usr/bin/env NAMErende la shell la ricerca della prima corrispondenza di NAME nella variabile di ambiente $ PATH. Può essere utile se non sei a conoscenza del percorso assoluto o non vuoi cercarlo.


9
Almeno devi sapere dove si trova env :).
ᐅ devrimbaris

1
Risposta eccellente. Spiega in modo succinto cosa fa l'env shebang, piuttosto che dire "sceglie il programma in base alla configurazione del sistema"
De Novo,

13

Invece di definire esplicitamente il percorso dell'interprete come in /usr/bin/bash/, utilizzando il comando env, l'interprete viene cercato e avviato da qualunque posizione venga trovata. Questo ha sia aspetti positivi che negativi


Sulla maggior parte dei sistemi, funzionalmente saranno gli stessi, ma dipende dalla posizione dei tuoi eseguibili bash ed env. Non sono sicuro di come ciò influirà sulle variabili di ambiente, tuttavia.
safay

2
"È possibile specificare l'interprete senza usare env, fornendo il percorso completo all'interprete. Un problema è che su diversi sistemi informatici, il percorso esatto può essere diverso. Invece usando env, l'interprete viene cercato e localizzato in il tempo di esecuzione dello script. Ciò rende lo script più portatile, ma aumenta anche il rischio che venga selezionato l'interprete sbagliato perché cerca una corrispondenza in ogni directory sul percorso di ricerca eseguibile. Soffre inoltre dello stesso problema in quanto il percorso del binario env può anche essere diverso per ogni macchina. "- Wikipedia
Mike Clark,

10

Se gli script della shell iniziano con #!/bin/bash, verranno sempre eseguiti con bashda /bin. Se tuttavia iniziano con #!/usr/bin/env bash, cercheranno bashin $PATHe poi inizieranno con il primo che riescono a trovare.

Perché sarebbe utile? Supponiamo che tu voglia eseguire bashscript che richiedono bash 4.xo bashversioni successive, ma il tuo sistema ha solo 3.x installato e attualmente la tua distribuzione non offre una versione più recente o non sei un amministratore e non puoi cambiare ciò che è installato su quel sistema .

Naturalmente, puoi scaricare il codice sorgente di bash e creare il tuo bash da zero, posizionandolo ad ~/binesempio. E puoi anche modificare la tua $PATHvariabile nel tuo .bash_profilefile per includerla ~/bincome prima voce ( PATH=$HOME/bin:$PATHpoiché ~non si espanderà $PATH). Se ora chiami bash, la shell lo cercherà prima $PATHin ordine, quindi inizia con ~/bindove troverà il tuo bash. La stessa cosa accade se gli script cercano di bashutilizzarli #!/usr/bin/env bash, quindi questi script ora funzionerebbero sul tuo sistema usando la tua bashbuild personalizzata .

Un aspetto negativo è che ciò può portare a comportamenti imprevisti, ad esempio lo stesso script sulla stessa macchina può essere eseguito con interpreti diversi per ambienti diversi o utenti con percorsi di ricerca diversi, causando ogni tipo di mal di testa.

Il più grande svantaggio envè che alcuni sistemi consentiranno solo un argomento, quindi non puoi farlo #!/usr/bin/env <interpreter> <arg>, poiché i sistemi vedranno <interpreter> <arg>come un argomento (lo tratteranno come se l'espressione fosse citata) e quindi envcercheranno un interprete chiamato <interpreter> <arg>. Si noti che questo non è un problema del envcomando stesso, che ha sempre consentito il passaggio di più parametri ma con il parser shebang del sistema che analizza questa linea prima di chiamare env. Nel frattempo questo è stato risolto sulla maggior parte dei sistemi, ma se il tuo script vuole essere ultra portatile, non puoi fare affidamento sul fatto che questo è stato corretto sul sistema che eseguirai.

Può anche avere implicazioni per la sicurezza, ad esempio se sudonon è stato configurato per pulire l'ambiente o è $PATHstato escluso dalla pulizia. Lasciami dimostrare questo:

Di solito /binè un posto ben protetto, rootè solo in grado di cambiare qualcosa lì. La tua home directory non è, tuttavia, nessun programma che esegui è in grado di apportarvi modifiche. Ciò significa che il codice dannoso potrebbe inserire un falso bashin una directory nascosta, modificarlo .bash_profileper includerlo nella propria directory $PATH, quindi tutti gli script utilizzati #!/usr/bin/env bashfiniranno per essere eseguiti con quel falso bash. Se sudocontinua $PATH, sei in grossi guai.

Ad esempio, considera che uno strumento crea un file ~/.evil/bashcon il seguente contenuto:

#!/bin/bash

if [ $EUID -eq 0 ]; then
  echo "All your base are belong to us..."
  # We are root - do whatever you want to do
fi

/bin/bash "$@"

Facciamo un semplice script sample.sh:

#!/usr/bin/env bash

echo "Hello World"

Prova del concetto (su un sistema in cui sudocontinua $PATH):

$ ./sample.sh
Hello World

$ sudo ./sample.sh
Hello World

$ export PATH="$HOME/.evil:$PATH"

$ ./sample.sh
Hello World

$ sudo ./sample.sh
All your base are belong to us...
Hello World

Di solito le shell classiche dovrebbero essere tutte localizzate /bine se non vuoi metterle lì per qualsiasi motivo, non è davvero un problema posizionare un link simbolico in /binquel punto verso le loro posizioni reali (o forse /binè esso stesso un link simbolico), quindi Vorrei sempre andare con #!/bin/she #!/bin/bash. C'è solo troppo che si spezzerebbe se non funzionasse più. Non è che POSIX richiederebbe questa posizione (POSIX non standardizza i nomi dei percorsi e quindi non uniforma affatto la funzione shebang) ma sono così comuni, che anche se un sistema non offrirebbe un /bin/sh, probabilmente capirà comunque #!/bin/she sapere cosa farne e potrebbe essere solo per compatibilità con il codice esistente.

Ma per interpreti più moderni, non standard, opzionali come Perl, PHP, Python o Ruby, non è specificato da nessuna parte dove dovrebbero trovarsi. Essi possono essere in /usr/binma possono anche essere in /usr/local/bino in un ramo gerarchia di completamente diverso ( /opt/..., /Applications/..., ecc). Ecco perché questi usano spesso la #!/usr/bin/env xxxsintassi di shebang.


4

Lo trovo utile, perché quando non sapevo di env, prima di iniziare a scrivere la sceneggiatura lo stavo facendo:

type nodejs > scriptname.js #or any other environment

e poi stavo modificando quella riga nel file in shebang.
Lo stavo facendo, perché non ricordavo sempre dove si trovasse nodejs sul mio computer - / usr / bin / o / bin /, quindi per me envè molto utile. Forse ci sono dettagli in questo, ma questa è la mia ragione

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.