Se gli script della shell iniziano con #!/bin/bash
, funzioneranno sempre con bash
da /bin
. Se invece iniziano con #!/usr/bin/env bash
, cercheranno bash
in $PATH
e quindi iniziare con il primo che riescono a trovare.
Perché questo sarebbe utile? Supponi di voler eseguire bash
script, che richiedono bash 4.xo più recente, ma il tuo sistema ha solo bash
3.x installato e attualmente la tua distribuzione non offre una versione più recente o non sei un amministratore e non puoi modificare ciò che è installato su quel sistema.
Naturalmente, puoi scaricare il codice sorgente di bash e creare la tua bash da zero, posizionandola in ~/bin
Per esempio. E puoi anche modificare il tuo $PATH
variabile nel tuo .bash_profile
file per includere ~/bin
come prima voce (PATH=$HOME/bin:$PATH
come ~
non si espanderà in $PATH
). Se ora chiami bash
, la shell lo cercherà prima in $PATH
in ordine, quindi inizia con ~/bin
, dove troverà il tuo bash
. La stessa cosa accade se gli script cercano bash
utilizzando #!/usr/bin/env bash
, quindi questi script ora funzionerebbero sul tuo sistema utilizzando il tuo bash
personalizzato costruire.
Uno svantaggio è che questo può portare a comportamenti imprevisti, ad es. 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 grattacapo.
Il più grande svantaggio con 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 env
cercherà un interprete chiamato <interpreter> <arg>
. Nota che questo non è un problema del env
comando stesso, che ha sempre consentito il passaggio di più parametri ma con il parser shebang del sistema che analizza questa riga prima ancora di chiamare env
. Nel frattempo, questo è stato corretto sulla maggior parte dei sistemi, ma se il tuo script vuole essere ultraportatile, non puoi fare affidamento sul fatto che sia stato risolto sul sistema che eseguirai.
Può anche avere implicazioni sulla sicurezza, ad es. if sudo
non è stato configurato per pulire l'ambiente o $PATH
è stato escluso dalla bonifica. Lasciatemi dimostrare questo:
Di solito /bin
è un luogo ben protetto, solo root
è in grado di cambiare qualsiasi cosa lì. La tua home directory non è, tuttavia, qualsiasi programma che esegui è in grado di apportare modifiche ad essa. Ciò significa che il codice dannoso potrebbe inserire un falso bash
in qualche directory nascosta, modifica il tuo .bash_profile
per includere quella directory nel tuo $PATH
, quindi tutti gli script utilizzano #!/usr/bin/env bash
finirà per funzionare con quel falso bash
. Se sudo
mantiene $PATH
, sei in grossi guai.
Per esempio. considera che uno strumento crea un file ~/.evil/bash
con 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 "[email protected]"
Facciamo un semplice script sample.sh
:
#!/usr/bin/env bash
echo "Hello World"
Proof of concept (su un sistema in cui sudo
mantiene $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 trovarsi tutte in /bin
e se non vuoi metterli lì per qualsiasi motivo, non è davvero un problema inserire un collegamento simbolico in /bin
che indica le loro posizioni reali (o forse /bin
è di per sé un collegamento simbolico), quindi andrei sempre con #!/bin/sh
e #!/bin/bash
. C'è solo troppo che si romperebbe se questi non funzionassero più. Non è che POSIX richieda queste posizioni (POSIX non standardizza i nomi di percorso e quindi non standardizza nemmeno la funzione shebang) ma sono così comuni che anche se un sistema non offrisse un /bin/sh
, probabilmente capirebbe ancora #!/bin/sh
e sapere cosa farne e che sia solo per compatibilità con il codice esistente.
Ma per interpreti più moderni, non standard e opzionali come Perl, PHP, Python o Ruby, non è realmente specificato da nessuna parte dove dovrebbero trovarsi. Potrebbero essere in /usr/bin
ma potrebbero anche essere in /usr/local/bin
o in un ramo gerarchico completamente diverso (/opt/...
, /Applications/...
, eccetera.). Ecco perché spesso usano il #!/usr/bin/env xxx
sintassi shebang.
Usando #!/usr/bin/env NAME
fa in modo che la shell cerchi la prima corrispondenza di NAME nella variabile d'ambiente $PATH. Può essere utile se non sei a conoscenza del percorso assoluto o non vuoi cercarlo.
Esecuzione di un comando tramite /usr/bin/env
ha il vantaggio di cercare qualunque sia la versione predefinita del programma nel tuo attuale env ferro.
In questo modo, non è necessario cercarlo in un punto specifico del sistema, poiché tali percorsi potrebbero trovarsi in posizioni diverse su sistemi diversi. Finché è sul tuo cammino, lo troverà.
Uno svantaggio è 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 denotare un singolo argomento. Puoi usare /usr/bin/env -S
su alcune versioni di env
per aggirare questo problema, ma poi lo script diventerà ancora meno portabile e si romperà su sistemi abbastanza recenti (ad esempio anche Ubuntu 16.04 se non successivo).
Un altro svantaggio è che dal momento che non stai chiamando un eseguibile esplicito, ha il potenziale per errori e su sistemi multiutente problemi di sicurezza (se qualcuno è riuscito a ottenere il proprio eseguibile chiamato bash
nel tuo percorso, per 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, potrebbe essere preferibile il primo (come l'esecuzione di script Python con più versioni di Python, senza dover rielaborare la riga eseguibile). Ma in situazioni in cui la sicurezza è l'obiettivo, quest'ultimo sarebbe preferito, in quanto limita le possibilità di iniezione di codice.