Perché le persone scrivono il #! / Usr / bin / env python shebang sulla prima riga di uno script Python?


1047

Mi sembra che i file funzionino allo stesso modo senza quella linea.


1
La risposta sotto che afferma che è solo una riga di commento. Non è sempre così. Ho un "Ciao, mondo!" Script CGI (.py) che eseguirà e visualizzerà solo la pagina Web #!/usr/bin/env pythonin alto.
Chakotay,


Possono funzionare, ma non nell'ambiente previsto
Nicholas Hamilton,

18
Ho visitato questo post così tante volte in 7 anni perché a volte dimentico l'hashtag env. Copia pasta :)
BugHunterUK il

Risposte:


1083

Se hai installato diverse versioni di Python, /usr/bin/envassicurati che l'interprete utilizzato sia il primo nel tuo ambiente $PATH. L'alternativa sarebbe codificare qualcosa del genere #!/usr/bin/python; va bene, ma meno flessibile.

In Unix, un file eseguibile che deve essere interpretato può indicare quale interprete utilizzare avendo un #!all'inizio della prima riga, seguito dall'interprete (e da eventuali flag di cui potrebbe aver bisogno).

Se stai parlando di altre piattaforme, ovviamente, questa regola non si applica (ma quella "linea shebang" non fa male e ti aiuterà se copi mai quello script su una piattaforma con una base Unix, come Linux, Mac , eccetera).


267
Solo per aggiungere: questo vale quando lo esegui in Unix rendendolo eseguibile ( chmod +x myscript.py) e quindi eseguendolo direttamente:, ./myscript.pypiuttosto che solo python myscript.py.
Craig McQueen,

28
l'utilizzo envoffre la massima flessibilità in quanto l'utente può selezionare l'interprete da utilizzare modificando il PERCORSO. Spesso questa flessibilità non è richiesta e il rovescio della medaglia è che Linux, ad esempio, non può usare il nome dello script per il nome del processo in pse ripristina "python". Ad esempio, nel confezionare app Python per distribuzioni, consiglio di non utilizzarle env.
pixelbeat

9
pylauncher può usare la linea shebang su Windows. È incluso in Python 3.3 o può essere installato in modo indipendente .
jfs,

6
Un importante avvertimento, il valore di ritorno di env alla fine scade. Che è improbabile che ti colpisca se stai eseguendo processi di breve durata. Tuttavia, ho avuto processi che muoiono con il messaggio /usr/bin/env: Key has expireddopo molte ore.
malaverdiere,

4
@malaverdiere puoi collegarti a qualche risorsa che spiega questo comportamento in scadenza? Non li trovo.
Michael,

266

Questa si chiama la linea Shebang . Come spiega la voce di Wikipedia :

Nell'informatica, uno shebang (chiamato anche hashbang, hashpling, pound bang o crunchbang) si riferisce ai caratteri "#!" quando sono i primi due caratteri in una direttiva interprete come prima riga di un file di testo. In un sistema operativo simile a Unix, il programma di caricamento programma prende la presenza di questi due caratteri come indicazione che il file è uno script e tenta di eseguire quello script utilizzando l'interprete specificato dal resto della prima riga nel file.

Vedi anche la voce Domande frequenti su Unix .

Anche su Windows, dove la linea shebang non determina l'interprete da eseguire, è possibile passare le opzioni all'interprete specificandole sulla linea shebang. Trovo utile mantenere una linea shebang generica in script una tantum (come quelli che scrivo quando rispondo a domande su SO), quindi posso testarli rapidamente su Windows e ArchLinux .

L' utility env ti consente di invocare un comando sul percorso:

Il primo argomento rimanente specifica il nome del programma da richiamare; viene cercato in base alla PATHvariabile di ambiente. Eventuali argomenti rimanenti vengono passati come argomenti a quel programma.


30
Facile da trovare con Google - se si conoscono le parole chiave (la "linea shebang" è essenziale).
Arafangion,

14
In realtà, questa spiegazione è più chiara di altri riferimenti che ho verificato con Google. È sempre più bello ottenere una spiegazione di 1 paragrafo mirata alla domanda, piuttosto che leggere un intero manuale che affronta ogni potenziale utilizzo.
Sam Goldberg,

1
@Arafangion probabilmente troverai questa domanda utile. TL; DR: symbolhound.com
ulidtko

@ulidtko: interessante motore di ricerca, considera la possibilità di scrivere una risposta in modo che la domanda di john garcias abbia una risposta migliore.
Arafangion,

1
"Anche su Windows, dove la linea shebang non determina l'interprete da eseguire, puoi passare le opzioni all'interprete specificandole sulla linea shebang." Questo è semplicemente falso; se succede una cosa del genere, è perché l'interprete stesso sta elaborando la linea shebang. Se l'interprete non ha un riconoscimento speciale per le linee shebang, allora non succede nulla del genere. Windows non fa nulla con le linee shebang. "Quello che potresti descrivere in questo caso è il lanciatore di python: python.org/dev/peps/pep-0397 .
Kaz

154

Espandendo un po 'le altre risposte, ecco un piccolo esempio di come i tuoi script da riga di comando possono mettersi nei guai con un uso incauto delle /usr/bin/envlinee shebang:

$ /usr/local/bin/python -V
Python 2.6.4
$ /usr/bin/python -V
Python 2.5.1
$ cat my_script.py 
#!/usr/bin/env python
import json
print "hello, json"
$ PATH=/usr/local/bin:/usr/bin
$ ./my_script.py 
hello, json
$ PATH=/usr/bin:/usr/local/bin
$ ./my_script.py 
Traceback (most recent call last):
  File "./my_script.py", line 2, in <module>
    import json
ImportError: No module named json

Il modulo json non esiste in Python 2.5.

Un modo per evitare questo tipo di problema è utilizzare i nomi dei comandi python con versione che sono generalmente installati con la maggior parte dei Python:

$ cat my_script.py 
#!/usr/bin/env python2.6
import json
print "hello, json"

Se devi solo distinguere tra Python 2.xe Python 3.x, anche le versioni recenti di Python 3 forniscono un python3nome:

$ cat my_script.py 
#!/usr/bin/env python3
import json
print("hello, json")

27
Hmm, non è quello che ho tirato fuori da quel post.
Glenn Jackman,

1
Differenza tra locale e globale. Se which pythonritorna /usr/bin/python, un percorso di directory locale potrebbe essere difficile codificato: #!/usr/bin/python. Ma questo è meno flessibile di quello #!/usr/bin/env pythonche ha un'applicazione globale.
Noobninja,

85

Per eseguire lo script Python, dobbiamo dire alla shell tre cose:

  1. Che il file sia uno script
  2. Quale interprete vogliamo eseguire lo script
  3. Il percorso di detto interprete

Lo shebang #!compie (1.). Lo shebang inizia con un #perché il #personaggio è un marcatore di commento in molti linguaggi di scripting. Il contenuto della linea shebang viene quindi automaticamente ignorato dall'interprete.

Il envcomando esegue (2.) e (3.). Per citare "grawity",

Un uso comune del envcomando è quello di avviare gli interpreti, sfruttando il fatto che env cercherà $ PATH per il comando che gli viene detto di lanciare. Poiché la linea shebang richiede un percorso assoluto da specificare e poiché la posizione di vari interpreti (perl, bash, python) può variare molto, è comune usare:

#!/usr/bin/env perl  invece di provare a indovinare se si tratta di / bin / perl, / usr / bin / perl, / usr / local / bin / perl, / usr / local / pkg / perl, / fileserver / usr / bin / perl o / home / MrDaniel / usr / bin / perl sul sistema dell'utente ...

D'altra parte, env è quasi sempre in / usr / bin / env. (Tranne nei casi in cui non lo è; alcuni sistemi potrebbero utilizzare / bin / env, ma questa è un'occasione abbastanza rara e si verifica solo su sistemi non Linux.)


1
"grawity" dove?
Pacerier,

44

Forse la tua domanda è in questo senso:

Se vuoi usare: $python myscript.py

Non hai affatto bisogno di quella linea. Il sistema chiamerà python e quindi l'interprete python eseguirà il tuo script.

Ma se hai intenzione di usare: $./myscript.py

Chiamandolo direttamente come un normale programma o script bash, è necessario scrivere quella riga per specificare al sistema quale programma utilizzare per eseguirlo (e anche renderlo eseguibile con chmod 755)


oppure puoi scrivere python3 myscript.py
vijay shanker il

44

La execchiamata di sistema del kernel Linux comprende #!nativamente shebangs ( )

Quando fai su bash:

./something

su Linux, questo chiama la execchiamata di sistema con il percorso ./something.

Questa riga del kernel viene chiamata sul file passato a exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Legge i primi byte del file e li confronta con #!.

Se il confronto è vero, il resto della riga viene analizzato dal kernel Linux, che effettua un'altra execchiamata con percorso /usr/bin/env pythone file corrente come primo argomento:

/usr/bin/env python /path/to/script.py

e questo funziona con qualsiasi linguaggio di scripting che utilizza #come carattere di commento.

E sì, puoi fare un ciclo infinito con:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash riconosce l'errore:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! sembra essere leggibile dall'uomo, ma non è necessario.

Se il file si avviava con byte diversi, la execchiamata di sistema utilizzava un gestore diverso. L'altro gestore incorporato più importante è per i file eseguibili ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 che verifica la presenza di byte 7f 45 4c 46(che risulta essere umano leggibile per .ELF). Confermiamo leggendo i primi 4 byte di /bin/ls, che è un eseguibile ELF:

head -c 4 "$(which ls)" | hd 

produzione:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Quindi quando il kernel vede quei byte, prende il file ELF, lo mette in memoria correttamente e avvia un nuovo processo con esso. Vedi anche: In che modo il kernel ottiene un file binario eseguibile in esecuzione su Linux?

Infine, puoi aggiungere i tuoi gestori shebang con il binfmt_miscmeccanismo. Ad esempio, è possibile aggiungere un gestore personalizzato per i .jarfile . Questo meccanismo supporta anche i gestori per estensione del file. Un'altra applicazione è eseguire in modo trasparente eseguibili di un'architettura diversa con QEMU .

Tuttavia , non credo che POSIX specifichi shebang: https://unix.stackexchange.com/a/346214/32558 , anche se menzionato nelle sezioni logiche e nel modulo "se gli script eseguibili sono supportati dal sistema qualcosa potrebbe accadere". macOS e FreeBSD sembrano comunque implementarlo.

PATH cerca la motivazione

Probabilmente, una grande motivazione per l'esistenza di shebangs è il fatto che in Linux, spesso vogliamo eseguire comandi PATHproprio come:

basename-of-command

invece di:

/full/path/to/basename-of-command

Ma poi, senza il meccanismo shebang, come farebbe Linux a sapere come avviare ogni tipo di file?

Hardcoding dell'estensione nei comandi:

 basename-of-command.py

o implementando la ricerca PATH su ogni interprete:

python basename-of-command

sarebbe una possibilità, ma questo ha il problema maggiore che tutto si rompe se decidessimo mai di rifattare il comando in un'altra lingua.

Shebangs risolve questo problema magnificamente.


39

Tecnicamente, in Python, questa è solo una riga di commento.

Questa riga viene utilizzata solo se si esegue lo script py dalla shell (dalla riga di comando). Questo è noto come " Shebang !" e viene utilizzato in varie situazioni, non solo con gli script Python.

Qui, indica alla shell di avviare una versione specifica di Python (per occuparsi del resto del file.


Lo shebang è un concetto Unix. Vale la pena ricordare che funziona anche su Windows se hai installato il launcher Python py.exe . Questo fa parte di un'installazione standard di Python.
florisla,

38

Il motivo principale per farlo è rendere lo script portatile in tutti gli ambienti del sistema operativo.

Ad esempio in mingw, gli script python usano:

#!/c/python3k/python 

e sotto distribuzione GNU / Linux è:

#!/usr/local/bin/python 

o

#!/usr/bin/python

e sotto il miglior sistema commerciale Unix sw / hw di tutti (OS / X), è:

#!/Applications/MacPython 2.5/python

o su FreeBSD:

#!/usr/local/bin/python

Tuttavia, tutte queste differenze possono rendere lo script portatile in tutto usando:

#!/usr/bin/env python

2
Sotto MacOSX, lo è anche /usr/bin/python. Sotto Linux, anche il Python installato dal sistema è quasi certamente /usr/bin/python(non ho mai visto nient'altro e non avrebbe senso). Nota che potrebbero esserci sistemi che non hanno /usr/bin/env.
Albert,

1
Se sei su OSX e usi Homebrew e segui le loro istruzioni di installazione predefinite, sarà sotto #! / Usr / local / bin / python
Sarà il

@ Jean-PaulCalderone: vedi la risposta di saaj di seguito.
pydsigner,

Aggiornamento per l'anno 2018: Bare pythonnon è portatile, è interprete Python predefinito di distribuzione. Arch Linux utilizza Python 3 per impostazione predefinita per molto tempo e potrebbero anche pensarci le distribuzioni perché Python 2 è supportato solo fino al 2020.
mati865,

22

Probabilmente ha senso enfatizzare una cosa che la maggior parte ha perso, che può impedire una comprensione immediata. Quando si digita il pythonterminale normalmente non si fornisce un percorso completo. Al contrario, l'eseguibile viene cercato nella PATHvariabile d'ambiente. A sua volta, quando si desidera eseguire direttamente un programma Python /path/to/app.py, si deve dire alla shell quale interprete usare (tramite l' hashbang , cosa spiegano gli altri contributori sopra).

Hashbang prevede il percorso completo per un interprete. Pertanto, per eseguire direttamente il tuo programma Python devi fornire un percorso completo al binario Python che varia in modo significativo, specialmente considerando l'uso di virtualenv . Per affrontare la portabilità /usr/bin/envviene utilizzato il trucco . Quest'ultimo è originariamente destinato a modificare l'ambiente sul posto ed eseguire un comando al suo interno. Quando non viene fornita alcuna modifica, viene eseguito il comando nell'ambiente corrente, il che si traduce effettivamente nella stessa PATHricerca che fa il trucco.

Sorgente da unix stackexchange


14

Questa è una convenzione della shell che dice alla shell quale programma può eseguire lo script.

#! / usr / bin / env python

si risolve in un percorso del binario Python.



9

Puoi provare questo problema usando virtualenv

Ecco test.py

#! /usr/bin/env python
import sys
print(sys.version)

Crea ambienti virtuali

virtualenv test2.6 -p /usr/bin/python2.6
virtualenv test2.7 -p /usr/bin/python2.7

attiva ogni ambiente quindi controlla le differenze

echo $PATH
./test.py

9

Specifica solo quale interprete si desidera utilizzare. Per capirlo, crea un file attraverso il terminale facendo touch test.py, quindi digita in quel file quanto segue:

#!/usr/bin/env python3
print "test"

e fai chmod +x test.pyper rendere eseguibile il tuo script. Dopo questo quando lo fai ./test.pydovresti ricevere un errore che dice:

  File "./test.py", line 2
    print "test"
               ^
SyntaxError: Missing parentheses in call to 'print'

perché python3 non supporta l'operatore di stampa.

Ora vai avanti e modifica la prima riga del codice in:

#!/usr/bin/env python2

e funzionerà, stampando testsu stdout, perché python2 supporta l'operatore di stampa. Quindi, ora hai imparato a passare da un interprete di script all'altro.


9

Mi sembra che i file funzionino allo stesso modo senza quella linea.

Se è così, forse stai eseguendo il programma Python su Windows? Windows non utilizza quella linea, ma utilizza l'estensione del nome file per eseguire il programma associato all'estensione del file.

Tuttavia, nel 2011, è stato sviluppato un "launcher Python" che (in una certa misura) imita questo comportamento di Linux per Windows. Ciò si limita solo alla scelta dell'interprete Python da eseguire, ad esempio per selezionare tra Python 2 e Python 3 su un sistema in cui entrambi sono installati. Il programma di avvio è facoltativamente installato come py.exedall'installazione di Python e può essere associato ai .pyfile in modo che il programma di avvio controllerà quella linea e, a sua volta, avvierà la versione dell'interprete Python specificata.


6
Potrebbe anche usare $ python myscript.py.
Sinan Ünür

Ho fatto l'errore non avendo la linea e ho usato python script.py, e un giorno ho appena fatto ./myscript.py e tutto ha smesso di funzionare, poi ho realizzato che il sistema sta guardando il file come uno script shell invece che uno script Python.
Guagua,

8

Questo significa più informazioni storiche che una risposta "reale".

Ricorda che nel passato avevi MOLTO sistemi unix come i sistemi operativi i cui progettisti avevano tutti la propria idea di dove mettere le cose, e a volte non includevano Python, Perl, Bash o molte altre cose GNU / Open Source affatto .

Questo era vero anche per le diverse distribuzioni Linux. Su Linux - pre-FHS [1] - potresti avere Python in / usr / bin / o / usr / local / bin /. Oppure potrebbe non essere stato installato, quindi hai creato il tuo e lo hai inserito in ~ / bin

Solaris è stato il peggiore su cui abbia mai lavorato, in parte come il passaggio da Berkeley Unix a System V. Potresti finire con le cose in / usr /, / usr / local /, / usr / ucb, / opt / ecc. Questo potrebbe rendere per alcuni percorsi davvero lunghi. Ho ricordi delle cose di Sunfreeware.com che installavano ciascun pacchetto nella sua directory, ma non ricordo se collegasse i binari in / usr / bin o meno.

Oh, e talvolta / usr / bin era su un server NFS [2].

Quindi l' envutilità è stata sviluppata per aggirare questo problema.

Quindi potresti scrivere #!/bin/env interpretere fintanto che il percorso era corretto le cose avevano una ragionevole possibilità di correre. Naturalmente, ragionevole significava (per Python e Perl) che avevate anche impostato le variabili ambientali appropriate. Per bash / ksh / zsh ha funzionato.

Questo era importante perché le persone passavano per gli script di shell (come perl e python) e se avessi hard coded / usr / bin / python sulla tua stazione di lavoro Red Hat Linux si sarebbe rotto male su un SGI ... beh, no , Penso che IRIX abbia messo Python nel posto giusto. Ma su una stazione Sparc potrebbe non funzionare affatto.

Mi manca la mia stazione sparc. Ma non molto. Ok, ora mi fai andare a pesca a traina su E-Bay. Bastages.

[1] Standard della gerarchia del file system. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Sì, e talvolta le persone fanno ancora cose del genere. E no, non indossavo né una rapa né una cipolla sulla cintura.


5

Se stai eseguendo il tuo script in un ambiente virtuale, ad esempio venv, eseguendolo which pythonmentre lavori venvvisualizzerà il percorso dell'interprete Python:

~/Envs/venv/bin/python

Si noti che il nome dell'ambiente virtuale è incorporato nel percorso dell'interprete Python. Pertanto, codificare questo percorso nello script causerà due problemi:

  • Se carichi lo script in un repository, stai forzando altri utenti ad avere lo stesso nome di ambiente virtuale . Questo se identificano prima il problema.
  • Non sarai in grado di eseguire lo script su più ambienti virtuali anche se avessi tutti i pacchetti richiesti in altri ambienti virtuali.

Pertanto, per aggiungere alla risposta di Jonathan , lo shebang ideale è #!/usr/bin/env pythonnon solo per la portabilità su tutti i sistemi operativi, ma anche per la portabilità in ambienti virtuali!


3

Considerando i problemi di portabilità tra python2e python3, è necessario specificare sempre una delle versioni a meno che il programma non sia compatibile con entrambe.

Alcune distribuzioni sono in pythoncollegamento simbolico da python3un po 'di tempo - non fare affidamento pythonsull'essere python2.

Ciò è sottolineato da PEP 394 :

Per tollerare le differenze tra le piattaforme, tutto il nuovo codice che deve invocare l'interprete Python non deve specificare python, ma piuttosto specificare python2 o python3 (o le versioni più specifiche di python2.xe python3.x; consultare le Note sulla migrazione ) . Questa distinzione dovrebbe essere fatta in shebang, quando si invoca da uno script di shell, quando si invoca tramite la chiamata di sistema () o quando si invoca in qualsiasi altro contesto.


2

Indica all'interprete con quale versione di Python eseguire il programma quando si hanno più versioni di Python.


0

Ti consente di selezionare l'eseguibile che desideri utilizzare; che è molto utile se forse hai più installazioni di Python e moduli diversi in ognuna e desideri scegliere. per esempio

#!/bin/sh
#
# Choose the python we need. Explanation:
# a) '''\' translates to \ in shell, and starts a python multi-line string
# b) "" strings are treated as string concat by python, shell ignores them
# c) "true" command ignores its arguments
# c) exit before the ending ''' so the shell reads no further
# d) reset set docstrings to ignore the multiline comment code
#
"true" '''\'
PREFERRED_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7/bin/python
ALTERNATIVE_PYTHON=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
FALLBACK_PYTHON=python3

if [ -x $PREFERRED_PYTHON ]; then
    echo Using preferred python $ALTERNATIVE_PYTHON
    exec $PREFERRED_PYTHON "$0" "$@"
elif [ -x $ALTERNATIVE_PYTHON ]; then
    echo Using alternative python $ALTERNATIVE_PYTHON
    exec $ALTERNATIVE_PYTHON "$0" "$@"
else
    echo Using fallback python $FALLBACK_PYTHON
    exec python3 "$0" "$@"
fi
exit 127
'''

__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())

-8

questo dice allo script dov'è la directory di Python!

#! /usr/bin/env python
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.