Waarom schrijven mensen #!/usr/bin/env python op de eerste regel van een Python-script?

Het lijkt mij alsof de bestanden hetzelfde werken zonder die regel.


Antwoord 1, autoriteit 100%

Als je meerdere versies van Python hebt geïnstalleerd, zal /usr/bin/envervoor zorgen dat de gebruikte interpreter de eerste is op de $PATHvan je omgeving. Het alternatief zou zijn om iets als #!/usr/bin/pythonte hardcoderen; dat is ok, maar minder flexibel.

In Unix kan een uitvoerbaar-bestand dat bedoeld is om te worden geïnterpreteerd, aangeven welke interpreter moet worden gebruikt door een #!aan het begin van de eerste regel te plaatsen, gevolgd door de tolk (en eventuele vlaggen die het nodig heeft).

Als je het over andere platforms hebt, is deze regel natuurlijk niet van toepassing (maar die “shebang-regel” kan geen kwaad, en zal helpen als je dat script ooit naar een platform kopieert meteen Unix-basis, zoals Linux, Mac, enz.).


Antwoord 2, autoriteit 25%

Dat wordt de shebang-regelgenoemd. Zoals het Wikipedia-item uitlegt:

In computergebruik verwijst een shebang (ook wel hashbang, hashpling, pound bang of crunchbang genoemd) naar de tekens “#!” wanneer ze de eerste twee tekens in een tolkrichtlijn zijn als de eerste regel van een tekstbestand. In een Unix-achtig besturingssysteem neemt de programmalader de aanwezigheid van deze twee karakters als een indicatie dat het bestand een script is, en probeert dat script uit te voeren met behulp van de interpreter gespecificeerd door de rest van de eerste regel in het bestand.

Zie ook de Unix FAQ-invoer.

Zelfs in Windows, waar de shebang-regel niet bepaalt welke interpreter moet worden uitgevoerd, kunt u opties aan de interpreter doorgeven door ze op de shebang-regel te specificeren. Ik vind het handig om een generieke shebang-regel in eenmalige scripts te houden (zoals degene die ik schrijf bij het beantwoorden van vragen over SO), zodat ik ze snel kan testen op zowel Windows als ArchLinux.

Met het env-hulpprogrammakun je een commando op het pad aanroepen:

Het eerste overgebleven argument specificeert de programmanaam die moet worden aangeroepen; er wordt naar gezocht volgens de omgevingsvariabele PATH. Alle resterende argumenten worden als argumenten aan dat programma doorgegeven.


Antwoord 3, autoriteit 14%

Een beetje uitbreidend op de andere antwoorden, hier is een klein voorbeeld van hoe uw commandoregelscripts in de problemen kunnen komen door onvoorzichtig gebruik van /usr/bin/envshebang-regels:

$ /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

De json-module bestaat niet in Python 2.5.

Een manier om u tegen dat soort problemen te beschermen, is door de python-opdrachtnamen met versiebeheer te gebruiken die doorgaans bij de meeste Pythons worden geïnstalleerd:

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

Als u alleen onderscheid moet maken tussen Python 2.x en Python 3.x, bieden recente releases van Python 3 ook een python3-naam:

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

Antwoord 4, autoriteit 8%

Om het python-script uit te voeren, moeten we de shell drie dingen vertellen:

  1. Dat het bestand een script is
  2. Welke interpreter we het script willen uitvoeren
  3. Het pad van de tolk

De kreng #!volbrengt (1.). De shebang begint met een #omdat het teken #een commentaarmarkering is in veel scripttalen. De inhoud van de shebang-regel wordt daarom automatisch genegeerd door de interpreter.

De opdracht envvolbrengt (2.) en (3.). Om “dankbaarheid” te citeren

Een algemeen gebruik van de opdracht envis om tolken te starten door het maken van
gebruik van het feit dat env $PATH zal zoeken voor het commando dat wordt verteld
lanceren. Omdat de shebang-regel een absoluut pad vereist om te zijn
gespecificeerd, en aangezien de locatie van verschillende tolken (perl, bash,
python) kan veel variëren, het is gebruikelijk om te gebruiken:

#!/usr/bin/env perl  in plaats van te proberen te raden of het zo is
/bin/perl, /usr/bin/perl, /usr/local/bin/perl, /usr/local/pkg/perl,
/fileserver/usr/bin/perl, of /home/MrDaniel/usr/bin/perl op de
systeem…

Aan de andere kant staat env bijna altijd in /usr/bin/env. (Behalve in
gevallen waarin dat niet het geval is; sommige systemen kunnen /bin/env gebruiken, maar dat is een
vrij zeldzame gelegenheid en gebeurt alleen op niet-Linux-systemen.)


Antwoord 5, autoriteit 5%

De execsysteemaanroep van de Linux-kernel begrijpt shebangs (#!) native

Als je bash doet:

./something

op Linux roept dit de systeemaanroep execaan met het pad ./something.

Deze regel van de kernel wordt aangeroepen in het bestand dat is doorgegeven aan exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

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

Het leest de allereerste bytes van het bestand en vergelijkt ze met #!.

Als de vergelijking waar is, wordt de rest van de regel geparseerd door de Linux-kernel, die een andere exec-aanroep doet met:

  • uitvoerbaar: /usr/bin/env
  • eerste argument: python
  • tweede argument: scriptpad

dus gelijk aan:

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

envis een uitvoerbaar bestand dat PATHzoekt om b.v. zoek /usr/bin/pythonen roept ten slotte:

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

De Python-interpreter ziet de regel #!in het bestand, maar #is het commentaarteken in Python, dus die regel wordt gewoon genegeerd als een gewone opmerking.

En ja, je kunt een oneindige lus maken met:

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

bash herkent de fout:

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

#!is toevallig menselijk leesbaar, maar dat is niet vereist.

Als het bestand met verschillende bytes is gestart, zou de execSysteemoproep een andere handler gebruiken. De andere belangrijkste ingebouwde handler is voor ELF-uitvoerbare bestanden: HTTPS://github.com/torValds/LINUX/BOB/V4.8/FS/BINFMT_ELF.C#L1305 welke controleert op bytes 7f 45 4c 46(die ook gebeurt om menselijk leesbaar te zijn voor .ELF). Laten we bevestigen dat door de 4 eerste bytes van /bin/ls, een ELF-uitvoerbaar bestand is:

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

Uitgang:

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

Dus wanneer de kernel die bytes ziet, neemt het het ELF-bestand in, plaatst deze correct in het geheugen en begint er een nieuw proces mee. Zie ook: Hoe is kernel Ontvang een uitvoerbaar binair bestand dat onder Linux wordt uitgevoerd?

Ten slotte kunt u uw eigen Shebang-handlers toevoegen met de binfmt_miscmechanisme. U kunt bijvoorbeeld een aangepaste handler voor .jarBestanden . Dit mechanisme ondersteunt zelfs handlers per bestandsextensie. Een andere applicatie is bij transparant uitvoeren van executables van een andere architectuur met Qemu .

Ik denk niet posix Specificeert Shebangs echter : https://unix.stackexchange.com/a/346214/32558 , hoewel het vermeldt in Rationale Secties, en in het formulier “als uitvoerbare scripts worden ondersteund door het systeem dat er iets kan gebeuren”. MacOS en FreeBSD lijken echter ook te implementeren.

PATHZoek motivatie

Waarschijnlijk is een grote motivatie voor het bestaan ​​van Shebangs het feit dat we in Linux vaak opdrachten willen uitvoeren van PATHnet als:

basename-of-command

In plaats van:

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

Maar dan, zonder het Shebang-mechanisme, hoe zou Linux weten hoe u elk type bestand kunt starten?

Hardcoding De extensie in opdrachten:

basename-of-command.py

of implementatiepad zoeken op elke tolk:

python basename-of-command

zou een mogelijkheid zijn, maar dit heeft het grote probleem dat alles kapot gaat als we ooit besluiten om het commando in een andere taal te refactoreren.

Kepers lossen dit probleem prachtig op.

Belangrijk gebruik van env: pyenven andere versiebeheerders

Een belangrijk gebruiksvoorbeeld van waarom u #!/usr/bin/env pythonzou moeten gebruiken in plaats van alleen /usr/bin/pythonis dat van versiebeheerders met pyenv.

Met

pyenvkunt u eenvoudig meerdere python-versies op één machine installeren, zodat u andere projecten beter kunt reproduceren zonder virtualisatie.

Vervolgens beheert het de “huidige” pythonversie door de volgorde in het PATH in te stellen: b.v. zoals getoond op apt-get install voor verschillende python-versieseen door pyenv beheerde python kan zich bevinden op:

/home/ciro/.pyenv/shims/python

dus nergens in de buurt van /usr/bin/python, waarmee sommige systemen kunnen omgaan via update-alternativessymbolische links.


Antwoord 6, autoriteit 4%

Misschien is uw vraag in deze zin:

Als je het volgende wilt gebruiken: $python myscript.py

Je hebt die regel helemaal niet nodig. Het systeem roept python aan en vervolgens zal de python-interpreter je script uitvoeren.

Maar als u van plan bent het volgende te gebruiken: $./myscript.py

Roept u rechtstreeks als een normaal programma of een bash-script, u ​​hoeft die regel te schrijven om op te geven aan het systeem dat het programma gebruikt om het uit te voeren (en maakt het ook uitvoerbaar bestand met chmod 755)


7, Autoriteit 3%

Technisch gezien, in Python, dit is slechts een reactie-lijn.

Deze regel wordt alleen gebruikt als u het PY-script van de Shell (van de opdrachtregel) uitvoert. Dit is bekend als de shebang !” , en Het wordt in verschillende situaties gebruikt, niet alleen met Python-scripts.

Hier geeft het de schaal op om een ​​specifieke versie van Python te starten (om voor de rest van het bestand te zorgen.


8, Autoriteit 3%

De belangrijkste reden om dit te doen is om het script draagbaar te maken over het besturingssysteemomgevingen.

Bijvoorbeeld onder MingW, Python-scripts gebruiken:

#!/c/python3k/python 

en onder GNU / Linux-distributie is het:

#!/usr/local/bin/python 

of

#!/usr/bin/python

en onder het beste commerciële Unix SW / HW-systeem van alle (OS / X), is het:

#!/Applications/MacPython 2.5/python

of op FreeBSD:

#!/usr/local/bin/python 

Maar al deze verschillen kunnen het script draagbaar maken door gebruik te maken van:

#!/usr/bin/env python

9, Autoriteit 2%

Het is waarschijnlijk logisch om één ding te benadrukken dat de meesten hebben gemist, wat onmiddellijk begrip in de weg kan staan. Wanneer u pythontypt in terminal, geeft u normaal gesproken geen volledig pad op. In plaats daarvan wordt het uitvoerbare bestand opgezocht in de omgevingsvariabele PATH. Als je op zijn beurt een Python-programma rechtstreeks wilt uitvoeren, /path/to/app.py, moet je de shell vertellen welke interpreter moet worden gebruikt (via de hashbang, wat de andere bijdragers hierboven uitleggen).

Hashbang verwacht het volledige padnaar een tolk. Dus om je Python-programma rechtstreeks uit te voeren, moet je het volledige pad naar het binaire bestand van Python opgeven, wat aanzienlijk varieert, vooral gezien het gebruik van virtualenv. Om portabiliteit aan te pakken wordt de truc met /usr/bin/envgebruikt. De laatste is oorspronkelijk bedoeld om de omgeving ter plaatse te wijzigen en er een opdracht in uit te voeren. Als er geen wijziging wordt aangebracht, voert het de opdracht uit in de huidige omgeving, wat in feite resulteert in dezelfde PATH-zoekopdracht die de truc doet.

Bron van unix stackexchange


Antwoord 10

Dit is een shell-conventie die de shell vertelt welk programma het script kan uitvoeren.

#!/usr/bin/env python

wordt opgelost in een pad naar het binaire bestand van Python.


Antwoord 11

Het is de aanbevolen manier, voorgesteld in de documentatie:

2.2.2. Uitvoerbare Python-scripts

Op BSD-achtige Unix-systemen kunnen Python-scripts direct worden gemaakt
uitvoerbaar, zoals shellscripts, door de regel te plaatsen

#! /usr/bin/env python3.2

van http://docs.python.org/py3k /tutorial/interpreter.html#executable-python-scripts


Antwoord 12

U kunt dit probleem proberen met virtualenv

Hier is test.py

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

Virtuele omgevingen maken

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

activeer elke omgeving en controleer de verschillen

echo $PATH
./test.py

Antwoord 13

Het geeft alleen aan welke interpreter u wilt gebruiken. Om dit te begrijpen, maakt u een bestand via terminal door touch test.pyte doen en typt u vervolgens het volgende in dat bestand:

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

en doe chmod +x test.pyom je script uitvoerbaar te maken. Hierna, wanneer u ./test.pydoet, zou u een foutmelding moeten krijgen met de tekst:

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

omdat python3 de printoperator niet ondersteunt.

Ga je gang en verander de eerste regel van je code in:

#!/usr/bin/env python2

en het zal werken door testaf te drukken naar stdout, omdat python2 de printoperator ondersteunt. Dus nu heb je geleerd hoe je kunt schakelen tussen scriptinterpreters.


Antwoord 14

Het lijkt mij alsof de bestanden hetzelfde werken zonder die regel.

Zo ja, gebruik je dan misschien het Python-programma op Windows? Windows gebruikt die regel niet, maar gebruikt de bestandsnaamextensie om het programma uit te voeren dat aan de bestandsextensie is gekoppeld.

Echterin 2011 een “Python launcher”is ontwikkeld die (tot op zekere hoogte) dit Linux-gedrag voor Windows nabootst. Dit is beperkt tot het kiezen welke Python-interpreter wordt uitgevoerd – b.v. om te kiezen tussen Python 2 en Python 3 op een systeem waarop beide zijn geïnstalleerd. Het opstartprogramma wordt optioneel geïnstalleerd als py.exedoor Python-installatie en kan worden gekoppeld aan .py-bestanden, zodat het opstartprogramma die regel controleert en op zijn beurt de opgegeven Python start tolkversie.


Antwoord 15

Dit is meer historische informatie dan een ‘echt’ antwoord.

Onthoud dat je vroeger VEEL Unix-achtige besturingssystemen had waarvan de ontwerpers allemaal hun eigen idee hadden van waar ze dingen moesten plaatsen, en die soms geen Python, Perl, Bash of tal van andere GNU/Open Source bevatten dingen helemaal.

Dit gold zelfs voor verschillende Linux-distributies. Op Linux–pre-FHS[1]- heb je misschien python in /usr/bin/ of /usr/local/bin/. Of het is misschien niet geïnstalleerd, dus je hebt het zelf gebouwd en in ~/bin

. geplaatst

Solaris was de slechtste waaraan ik ooit heb gewerkt, deels als de overgang van Berkeley Unix naar System V. Je zou kunnen eindigen met dingen in /usr/, /usr/local/, /usr/ucb, /opt/ etc. Dit kan voor sommige echtlange paden zorgen. Ik heb herinneringen aan de dingen van Sunfreeware.com die elk pakket in zijn eigen map installeerde, maar ik kan me niet herinneren of het de binaire bestanden in /usr/bin symlinkte of niet.

O, en soms stond /usr/bin op een NFS-server[2].

Dus het hulpprogramma envis ontwikkeld om dit te omzeilen.

Dan zou je #!/bin/env interpreterkunnen schrijven en zolang het pad juist was, hadden de dingen een redelijkekans om te werken. Natuurlijk betekende redelijk(voor Python en Perl) dat je ook de juiste omgevingsvariabelen had ingesteld. Voor bash/ksh/zsh werkte het gewoon.

Dit was belangrijk omdat mensen shellscripts doorgaven (zoals perl en python) en als je /usr/bin/python hard had gecodeerd op je Red Hat Linux-werkstation, zou het kapot gaan op een SGI… nou nee, ik denk dat IRIX python op de juiste plek heeft gezet. Maar op een Sparc-station werkt het misschien helemaal niet.

Ik mis mijn sparc-station. Maar niet veel. Ok, nu heb je me aan het trollen op E-Bay. Basten.

[1] Hiërarchiestandaard voor bestandssysteem. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Ja, en soms doen mensen dat soort dingen nog steeds. En nee, ik droeg geen raap OF ui aan mijn riem.


16

De regel #!/bin/bash/python3of #!/bin/bash/pythonGeeft aan welke Python Compiler te gebruiken is. Mogelijk hebt u meerdere Python-versies geïnstalleerd. Bijvoorbeeld
a.py:

#!/bin/bash/python3
print("Hello World")

is een python3-script en
b.py:

#!/bin/bash/python
print "Hello World"

is een Python 2.x-script
Om dit bestand te gebruiken ./a.pyof ./b.pyWORDT GEBRUIKT, MOET U DE PRESTATIES VOOR DE BESTANDIGHEDEN VOOR DE PRIVECTIE Geeft, anders zal het uitvoeren van Permission deniedfout.
Voor het geven van uitvoeringstoestemming,

chmod +x a.py

17

Gezien de draagbaarheidskwesties tussen python2en python3, moet u altijd van beide versie opgeven, tenzij uw programma compatibel is met beide.

Sommige distributies zijn verzending pythonSymlinked to python3Al een tijdje – Vertrouw nu niet op pythonZien python2.

Dit wordt benadrukt door pep 394 :

Om verschillen over platforms te tolereren, alle nieuwe code die
moet de python-interpreter oproepen, mag geen python opgeven, maar
eerder zou Python2 of Python3 (of des te specifieker moeten worden opgegeven
Python2.x en Python3.x-versies; Zie de migratie notities ). Dit
Onderscheid moet worden gemaakt in Shebangs, bij het aanroepen van een schaal
Script, bij het aanroepen van via het oproep van het systeem () of bij het aanroepen van in elke
andere context.


18

Het vertelt de tolk welke versie van Python het programma kan uitvoeren wanneer u meerdere versies van Python hebt.


19

Hiermee kunt u het uitvoerbare bestand selecteren dat u wilt gebruiken; wat erg is
Handig als je misschien meerdere Python-installaties hebt, en verschillende modules
in elk en willen kiezen. b.g

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

Antwoord 20

dit vertelt het script waar de python-map zich bevindt!

#! /usr/bin/env python

Other episodes