it-swarm.com.de

Warum schreiben die Leute das #! / Usr / bin / env python Shebang in die erste Zeile eines Python Skripts?

Mir scheint, dass die Dateien ohne diese Zeile gleich laufen.

953
john garcias

Wenn Sie mehrere Versionen von Python installiert haben, stellt /usr/bin/env sicher, dass der verwendete Interpreter der erste in der $PATH Ihrer Umgebung ist. Die Alternative wäre, etwas wie #!/usr/bin/python fest zu codieren; Das ist in Ordnung, aber weniger flexibel.

In Unix kann eine ausführbare Datei, die interpretiert werden soll, angeben, welcher Interpreter verwendet werden soll, indem am Anfang der ersten Zeile ein #! gefolgt von dem Interpreter (und allen Flags, die er möglicherweise enthält) steht brauchen).

Wenn Sie über andere Plattformen sprechen, gilt diese Regel natürlich nicht (aber diese "Shebang-Linie" schadet nicht und hilft, wenn Sie dieses Skript jemals auf eine Plattform kopieren mit einem Unix wie Linux, Mac usw.).

1016
Alex Martelli

Das nennt man die Shebang-Linie . Wie das Wikipedia-Eintrag erklärt :

Beim Rechnen bezieht sich ein Shebang (auch als Hashbang, Hashpling, Poundbang oder Crunchbang bezeichnet) auf die Zeichen "#!" Wenn es sich um die ersten beiden Zeichen in einer Interpreter-Direktive als erste Zeile einer Textdatei handelt. In einem Unix-ähnlichen Betriebssystem nimmt der Programmlader das Vorhandensein dieser beiden Zeichen als Hinweis darauf, dass die Datei ein Skript ist, und versucht, dieses Skript unter Verwendung des Interpreters auszuführen, der im Rest der ersten Zeile der Datei angegeben ist.

Siehe auch den Eintrag nix FAQ .

Selbst unter Windows, bei dem die Shebang-Zeile den auszuführenden Interpreter nicht bestimmt, können Sie Optionen an den Interpreter übergeben, indem Sie sie in der Shebang-Zeile angeben. Ich finde es nützlich, eine generische Shebang-Zeile in einmaligen Skripten beizubehalten (wie die, die ich bei der Beantwortung von Fragen zu SO geschrieben habe), damit ich sie schnell unter Windows und ArchLinux testen kann.

Mit dem Hilfsprogramm env können Sie einen Befehl auf dem Pfad aufrufen:

Das erste verbleibende Argument gibt den aufzurufenden Programmnamen an. Es wird nach der Umgebungsvariablen PATH gesucht. Alle verbleibenden Argumente werden als Argumente an dieses Programm übergeben.

251
Sinan Ünür

Im Folgenden finden Sie ein kleines Beispiel dafür, wie Ihre Befehlszeilenskripte durch die unachtsame Verwendung von /usr/bin/env Shebang-Zeilen in Schwierigkeiten geraten können:

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

Das json-Modul ist in Python 2.5 nicht vorhanden.

Eine Möglichkeit, sich vor solchen Problemen zu schützen, besteht darin, die versionierten python -Befehlsnamen zu verwenden, die normalerweise mit den meisten Pythons installiert werden:

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

Wenn Sie nur zwischen Python 2.x und Python 3.x unterscheiden müssen, enthalten neuere Versionen von Python 3 auch einen python3 -Namen:

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

Um das Skript python auszuführen, müssen wir der Shell drei Dinge mitteilen:

  1. Dass die Datei ein Skript ist
  2. Welchen Interpreter wollen wir das Skript ausführen
  3. Der Weg des Dolmetschers

Der Shebang #! schafft (1.). Der Shebang beginnt mit einem #, da das # -Zeichen in vielen Skriptsprachen eine Kommentarmarkierung darstellt. Der Inhalt der Shebang-Zeile wird daher vom Interpreter automatisch ignoriert.

Der Befehl env führt (2.) und (3.) aus. Um "grawity" zu zitieren,

Eine häufige Verwendung des Befehls env besteht darin, Interpreter zu starten, indem die Tatsache ausgenutzt wird, dass env $ PATH nach dem Befehl durchsucht, der gestartet werden soll. Da für die Shebang-Linie ein absoluter Pfad angegeben werden muss und die Position verschiedener Interpreter (Perl, Bash, Python) sehr unterschiedlich sein kann, wird häufig Folgendes verwendet:

#!/usr/bin/env Perl anstatt zu erraten, ob es sich um/bin/Perl,/usr/bin/Perl,/usr/lokal/bin/Perl,/usr/lokal/pkg/Perl,/fileserver/usr/bin/handelt Perl oder/home/MrDaniel/usr/bin/Perl auf dem System des Benutzers ...

Andererseits befindet sich env fast immer in/usr/bin/env. (Außer in Fällen, in denen dies nicht der Fall ist. Einige Systeme verwenden möglicherweise/bin/env. Dies ist jedoch eine eher seltene Gelegenheit und tritt nur auf Nicht-Linux-Systemen auf.)

82
Rose Perrone

Vielleicht ist Ihre Frage in diesem Sinne:

Wenn Sie verwenden möchten: $python myscript.py

Sie brauchen diese Leitung überhaupt nicht. Das System ruft python auf und dann python führt der Interpreter Ihr Skript aus.

Aber wenn du vorhast zu benutzen: $./myscript.py

Wenn Sie es direkt wie ein normales Programm oder ein Bash-Skript aufrufen, müssen Sie diese Zeile schreiben, um dem System anzugeben, mit welchem ​​Programm es ausgeführt wird (und um es mit chmod 755 ausführbar zu machen).

41
user3765197

Technisch gesehen ist dies in Python nur eine Kommentarzeile.

Diese Zeile wird nur verwendet, wenn Sie das Py-Skript über die Shell (über die Befehlszeile) ausführen. Dies wird als " Shebang !" bezeichnet und wird in verschiedenen Situationen verwendet, nicht nur mit Python Skripte.

Hier weist es die Shell an, eine bestimmte Version von Python zu starten (um den Rest der Datei zu erledigen).

39
mjv

Der Hauptgrund dafür ist, das Skript für alle Betriebssystemumgebungen portierbar zu machen.

Zum Beispiel unter mingw verwenden python Skripte:

#!/c/python3k/python 

und unter GNU/Linux Distribution ist es entweder:

#!/usr/local/bin/python 

oder

#!/usr/bin/python

und unter dem besten kommerziellen Unix-SW/HW-System von allen (OS/X) ist es:

#!/Applications/MacPython 2.5/python

oder auf FreeBSD:

#!/usr/local/bin/python

All diese Unterschiede können das Skript jedoch für alle portabel machen, indem Folgendes verwendet wird:

#!/usr/bin/env python
37

Der Systemaufruf exec des Linux-Kernels versteht shebangs (#!) nativ

Wenn Sie auf Bash machen:

./something

unter Linux wird der Systemaufruf exec mit dem Pfad ./something aufgerufen.

Diese Zeile des Kernels wird in der Datei aufgerufen, die an exec übergeben wird: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

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

Dies liest die allerersten Bytes der Datei und vergleicht sie mit #!.

Wenn dies zutrifft, wird der Rest der Zeile vom Linux-Kernel analysiert, der einen weiteren exec-Aufruf mit dem Pfad /usr/bin/env python und der aktuellen Datei als erstem Argument ausführt:

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

dies funktioniert für alle Skriptsprachen, die # als Kommentarzeichen verwenden.

Und ja, Sie können eine Endlosschleife machen mit:

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

Bash erkennt den Fehler:

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

#! ist nur für Menschen lesbar, aber das ist nicht erforderlich.

Wenn die Datei mit unterschiedlichen Bytes gestartet wird, verwendet der Systemaufruf exec einen anderen Handler. Der andere wichtige integrierte Handler ist für ausführbare ELF-Dateien vorgesehen: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 prüft auf Bytes 7f 45 4c 46 (der auch für .ELF lesbar ist). Dadurch wird die ELF-Datei gelesen, korrekt gespeichert und ein neuer Prozess damit gestartet. Siehe auch: Wie bringt der Kernel eine ausführbare Binärdatei unter Linux zum Laufen?

Schließlich können Sie Ihre eigenen Shebang-Handler mit dem Mechanismus binfmt_misc hinzufügen. Beispielsweise können Sie einen benutzerdefinierten Handler für .jar -Dateien hinzufügen. Dieser Mechanismus unterstützt sogar Handler nach Dateierweiterung. Eine andere Anwendung ist transparente Ausführung von ausführbaren Dateien einer anderen Architektur mit QEM .

Ich glaube jedoch nicht, dass POSIX shebangs spezifiziert: https://unix.stackexchange.com/a/346214/32558 , obwohl dies in den Begründungsabschnitten erwähnt wird , und in der Form "Wenn ausführbare Skripte vom System unterstützt werden, kann etwas passieren". MacOS und FreeBSD scheinen es jedoch ebenfalls zu implementieren.

PATH Suchmotivation

Wahrscheinlich ist eine große Motivation für die Existenz von Shebangs die Tatsache, dass wir unter Linux häufig Befehle aus PATH ausführen möchten, wie:

basename-of-command

anstatt von:

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

Aber wie würde Linux dann ohne den Shebang-Mechanismus wissen, wie man jeden Dateityp startet?

Hardcodierung der Erweiterung in Befehlen:

 basename-of-command.py

oder Implementierung der PATH-Suche für jeden Interpreter:

python basename-of-command

wäre eine Möglichkeit, aber dies hat das Hauptproblem, dass alles kaputt geht, wenn wir uns jemals entscheiden, den Befehl in eine andere Sprache umzugestalten.

Shebangs lösen dieses Problem wunderbar.

Es ist wahrscheinlich sinnvoll, eine Sache hervorzuheben, die die meisten übersehen haben, was ein sofortiges Verständnis verhindern könnte. Wenn Sie im Terminal python eingeben, geben Sie normalerweise keinen vollständigen Pfad an. Stattdessen wird die ausführbare Datei in der Umgebungsvariablen PATH nachgeschlagen. Wenn Sie ein Python -Programm direkt ausführen möchten, /path/to/app.py, müssen Sie der Shell über den -Hashbang mitteilen, welcher Interpreter verwendet werden soll. , was die anderen Mitwirkenden oben erklären).

Hashbang erwartet den vollständigen Pfad zu einem Interpreter. Um Ihr Python Programm direkt ausführen zu können, müssen Sie den vollständigen Pfad zu Python Binärdatei angeben, der erheblich variiert, insbesondere wenn virtualenv verwendet wird. . Um die Portabilität zu verbessern, wird der Trick mit /usr/bin/env verwendet. Letzteres ist ursprünglich dazu gedacht, die Umgebung direkt zu ändern und darin einen Befehl auszuführen. Wenn keine Änderung angegeben ist, wird der Befehl in der aktuellen Umgebung ausgeführt, was effektiv zu demselben PATH -Aufruf führt, der den Trick ausführt.

Quelle von Unix StackExchange

21
saaj

Es wird empfohlen, wie in der Dokumentation vorgeschlagen:

2.2.2. Ausführbare Python Skripte

Auf BSD-Unix-Systemen können Python Skripte wie Shell-Skripte direkt ausführbar gemacht werden, indem die Zeile eingefügt wird

#! /usr/bin/env python3.2

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

12

Dies ist eine Shell-Konvention, die der Shell mitteilt, welches Programm das Skript ausführen kann.

#!/usr/bin/env Python

wird in einen Pfad zur Python-Binärdatei aufgelöst.

11
Frank Krueger

Sie können dieses Problem mit virtualenv versuchen

Hier ist test.py

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

Erstellen Sie virtuelle Umgebungen

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

aktiviere jede Umgebung und überprüfe dann die Unterschiede

echo $PATH
./test.py
9
Sercan

Mir scheint, dass die Dateien ohne diese Zeile gleich laufen.

Wenn ja, führen Sie möglicherweise das Programm Python unter Windows aus? Windows verwendet diese Zeile nicht. Stattdessen wird die Dateinamenerweiterung verwendet, um das mit der Dateierweiterung verknüpfte Programm auszuführen.

Jedoch wurde 2011 ein "Python-Launcher" entwickelt, der (bis zu einem gewissen Grad) dieses Linux-Verhalten für Windows nachahmt. Dies beschränkt sich nur auf die Auswahl, welcher Python -Interpreter ausgeführt wird - z. Wählen Sie zwischen Python 2 und Python 3 auf einem System, auf dem beide installiert sind. Der Launcher wird optional als py.exe von Python -Installation installiert und kann .py -Dateien zugeordnet werden, sodass der Launcher diese Zeile überprüft und anschließend das angegebene Python Interpreter Version.

8
Craig McQueen

Es gibt nur an, welchen Interpreter Sie verwenden möchten. Um dies zu verstehen, erstellen Sie eine Datei über das Terminal, indem Sie touch test.py ausführen, und geben Sie dann Folgendes in diese Datei ein:

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

und mache chmod +x test.py, um dein Skript ausführbar zu machen. Wenn Sie danach ./test.py ausführen, sollte eine Fehlermeldung angezeigt werden:

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

weil python3 den drucker nicht unterstützt.

Jetzt gehen Sie vor und ändern Sie die erste Zeile Ihres Codes in:

#!/usr/bin/env python2

und es wird funktionieren, indem test auf stdout gedruckt wird, da python2 den Druckoperator unterstützt. Jetzt haben Sie also gelernt, wie Sie zwischen Skriptinterpretern wechseln.

6
Pavel

Dies ist eher eine historische Information als eine "echte" Antwort.

Denken Sie daran, dass Sie früher VIELE Unix - ähnliche Betriebssysteme hatten, deren Entwickler alle ihre eigene Vorstellung davon hatten, wo sie Sachen ablegen sollten, und manchmal Python, Perl, Bash oder viele andere GNU/Open Source - Sachen überhaupt .

Dies galt sogar für verschiedene Linux-Distributionen. Unter Linux - Pre-FHS [1] - haben Sie möglicherweise python in/usr/bin/oder/usr/local/bin /. Oder es wurde möglicherweise nicht installiert, also haben Sie Ihr eigenes erstellt und es in ~/bin abgelegt

Solaris war das Schlimmste, an dem ich je gearbeitet habe, teilweise als der Übergang von Berkeley Unix zu System V. Sie konnten Sachen in/usr /,/usr/local /,/usr/ucb,/opt/usw. erledigen für einige wirklich lange Wege. Ich habe Erinnerungen an das Material von Sunfreeware.com, das jedes Paket in einem eigenen Verzeichnis installiert, kann mich aber nicht erinnern, ob es die Binärdateien mit/usr/bin verknüpft hat oder nicht.

Oh, und manchmal befand sich/usr/bin auf einem NFS-Server [2].

Daher wurde das Hilfsprogramm env entwickelt, um dies zu umgehen.

Dann könntest du #!/bin/env interpreter schreiben und solange der Pfad richtig war, hatten die Dinge eine vernünftige Chance zu rennen. Natürlich bedeutete vernünftig (für Python und Perl), dass Sie auch die entsprechenden Umgebungsvariablen festgelegt hatten. Für bash/ksh/zsh hat es einfach funktioniert.

Dies war wichtig, da die Leute Shell-Skripte (wie Perl und Python) weitergaben und wenn Sie/usr/bin/python auf Ihrer Red Hat Linux-Workstation hartcodiert hätten, würde es auf einer SGI nicht funktionieren ... nun, nein Ich denke, IRIX hat python an die richtige Stelle gesetzt. Aber auf einer Sparc-Station könnte es überhaupt nicht laufen.

Ich vermisse meine Sparc-Station. Aber nicht viele. Ok, jetzt hast du mich in E-Bay herumtollen lassen. Bastagen.

[1] Dateisystem-Hierarchiestandard. https://en.wikipedia.org/wiki/Filesystem_Hierarchy_Standard

[2] Ja, und manchmal machen die Leute immer noch solche Sachen. Und nein, ich trug auch keine Rübe OR eine Zwiebel am Gürtel.

6
Petro

Wenn Sie Ihr Skript in einer virtuellen Umgebung ausführen, sagen Sie venv. Wenn Sie dann _which python_ ausführen, während Sie an venv arbeiten, wird der Pfad zum Python -Interpreter angezeigt:

~/Envs/venv/bin/python

Beachten Sie, dass der Name der virtuellen Umgebung in den Pfad zum Interpreter Python eingebettet ist . Das Hardcodieren dieses Pfads in Ihrem Skript verursacht daher zwei Probleme:

  • Wenn Sie das Skript in ein Repository hochladen, erzwingen Sie , dass andere Benutzer denselben Namen für die virtuelle Umgebung haben . Dies ist, wenn sie das Problem zuerst identifizieren.
  • Sie können das Skript nicht in mehreren virtuellen Umgebungen ausführen , selbst wenn Sie alle erforderlichen Pakete in anderen virtuellen Umgebungen installiert haben.

Um die Antwort von Jonathan zu ergänzen, ist der ideale Shebang #!/usr/bin/env python, nicht nur für die Portabilität Betriebssysteme, aber auch für die Portabilität in virtuellen Umgebungen!

5
Bruce Wayne

Angesichts der Portabilitätsprobleme zwischen python2 und python3 sollten Sie immer eine der beiden Versionen angeben, es sei denn, Ihr Programm ist mit beiden kompatibel.

Einige Distributionen liefern python seit einiger Zeit mit python3 in Verbindung - verlassen Sie sich nicht darauf, dass pythonpython2 ist.

Dies wird durch PEP 394 hervorgehoben:

Um Unterschiede zwischen Plattformen zu tolerieren, sollte in jedem neuen Code, der den Python -Interpreter aufrufen muss, nicht python angegeben werden, sondern entweder python2 oder python3 (oder die spezifischeren Versionen python2.x und python3.x) ; siehe Migrationshinweise ). Diese Unterscheidung sollte in Shebangs getroffen werden, wenn von einem Shell-Skript aus, über den system () -Aufruf oder in einem anderen Kontext aufgerufen wird.

3
Zulan

Es teilt dem Interpreter mit, mit welcher Version von python das Programm ausgeführt werden soll, wenn Sie mehrere Python-Versionen haben.

2
Katie T