1366 Stimmen

Warum schreibt man #!/usr/bin/env python in die erste Zeile eines Python-Skripts?

Ich sehe dies am Anfang der Python-Dateien:

  1. Für Python 2-Dateien

    #!/usr/bin/env python
  2. Für Python 3-Dateien

    #!/usr/bin/env python3

Ich habe den Eindruck, dass die Dateien auch ohne diese Zeile funktionieren.

1299voto

Alex Martelli Punkte 805329

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

Unter Unix ist ein ausführbar Datei, die interpretiert werden soll, kann angeben, welchen Interpreter sie verwenden soll, indem sie eine #! am Anfang der ersten Zeile, gefolgt von dem Interpreter (und den eventuell benötigten Flags).

Für andere Plattformen gilt diese Regel natürlich nicht (aber die "Shebang-Zeile" kann nicht schaden und ist hilfreich, wenn Sie das Skript jemals auf eine andere Plattform kopieren mit eine Unix-Basis, wie Linux, Mac usw.).

314voto

Sinan Ünür Punkte 114993

Dies wird als die Schlagzeile . Als die Der Wikipedia-Eintrag erklärt :

In der Informatik bezieht sich ein Shebang (auch Hashbang, Hashpling, Poundbang oder Crunchbang genannt) auf die Zeichen "#!", wenn sie die ersten beiden Zeichen in einer Interpreteranweisung als erste Zeile einer Textdatei sind. In einem Unix-ähnlichen Betriebssystem nimmt der Programmlader das Vorhandensein dieser beiden Zeichen als Hinweis darauf, dass es sich bei der Datei um ein Skript handelt, und versucht, dieses Skript mit dem Interpreter auszuführen, der durch den Rest der ersten Zeile in der Datei angegeben ist.

Siehe auch die Unix-FAQ-Eintrag .

Selbst unter Windows, wo die shebang-Zeile nicht den auszuführenden Interpreter bestimmt, können Sie dem Interpreter Optionen übergeben, indem Sie sie in der shebang-Zeile angeben. Ich finde es nützlich, eine generische shebang-Zeile in einmaligen Skripten (wie denen, die ich schreibe, wenn ich Fragen auf SO beantworte) beizubehalten, damit ich sie schnell sowohl unter Windows als auch unter ArchLinux .

En Umgebungsdienstprogramm ermöglicht es Ihnen, einen Befehl auf dem Pfad aufzurufen:

Das erste verbleibende Argument gibt den aufzurufenden Programmnamen an; er wird gemäß der PATH Umgebungsvariable. Alle verbleibenden Argumente werden als Argumente an dieses Programm übergeben.

173voto

Ned Deily Punkte 81039

Um ein wenig auf die anderen Antworten einzugehen, hier ein kleines Beispiel, wie Ihre Befehlszeilenskripte durch unvorsichtige Verwendung von /usr/bin/env Zeilen:

$ /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 existiert nicht in Python 2.5.

Eine Möglichkeit, sich gegen diese Art von Problemen zu schützen, ist die Verwendung der versionierten Python-Befehlsnamen, die 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, bieten neuere Versionen von Python 3 auch eine python3 Name:

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

104voto

Rose Perrone Punkte 58235

Um das Python-Skript ausführen zu können, müssen wir der Shell drei Dinge mitteilen:

  1. dass die Datei ein Skript ist
  2. Welcher Interpreter das Skript ausführen soll
  3. Der Pfad des besagten Dolmetschers

Das ganze Paket #! vollzieht (1.). Der Reigen beginnt mit einer # weil die # Zeichen ist in vielen Skriptsprachen eine Kommentar-Markierung. Der Inhalt der Shebang-Zeile wird daher vom Interpreter automatisch ignoriert.

En env Befehl erfüllt (2.) und (3.). An Zitat "grawity,"

Eine häufige Verwendung des env Befehl ist es, Interpreter zu starten, indem man dass env in $PATH nach dem Befehl sucht, den es starten soll. gestartet werden soll. Da die shebang-Zeile die Angabe eines absoluten Pfades angegeben werden muss, und da der Ort der verschiedenen Interpreter (Perl, Bash, python) sehr unterschiedlich sein kann, ist es üblich, diese zu verwenden:

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

Andererseits befindet sich env fast immer in /usr/bin/env. (Außer in Fällen, in denen dies nicht der Fall ist; einige Systeme können /bin/env verwenden, aber das ist ein aber das ist ein ziemlich seltener Fall und kommt nur auf Nicht-Linux-Systemen vor).

78voto

En exec Systemaufruf des Linux-Kernels versteht Shebangs ( #! ) von Haus aus

Wenn Sie auf Bash:

./something

unter Linux, ruft dies die exec Systemaufruf mit dem Pfad ./something .

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

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

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

Wenn der Vergleich wahr ist, wird der Rest der Zeile vom Linux-Kernel geparst, der eine weitere exec rufen Sie mit:

  • ausführbar: /usr/bin/env
  • erstes Argument: python
  • zweites Argument: Skriptpfad

daher gleichbedeutend mit:

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

env ist eine ausführbare Datei, die die PATH um z.B. zu finden /usr/bin/python und ruft dann schließlich an:

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

Der Python-Interpreter sieht die #! Zeile in der Datei, sondern # ist das Kommentarzeichen in Python, also wird diese Zeile einfach als normaler Kommentar ignoriert.

Und ja, man kann eine Endlosschleife mit machen:

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

Die Bash erkennt den Fehler:

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

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

Wenn die Datei mit anderen Bytes beginnt, dann wird die exec Systemaufruf würde einen anderen Handler verwenden. Der andere wichtige eingebaute Handler ist für ausführbare ELF-Dateien: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 die nach Bytes sucht 7f 45 4c 46 (die auch für Menschen lesbar ist .ELF ). Bestätigen wir das, indem wir die ersten 4 Bytes von /bin/ls die eine ausführbare ELF-Datei ist:

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

Ausgabe:

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

Wenn der Kernel also diese Bytes sieht, nimmt er die ELF-Datei, legt sie korrekt im Speicher ab und startet einen neuen Prozess damit. Siehe auch: Wie bekommt der Kernel eine ausführbare Binärdatei unter Linux zum Laufen?

Schließlich können Sie Ihre eigenen Shebang-Handler mit der Option binfmt_misc Mechanismus. Zum Beispiel können Sie eine benutzerdefinierter Handler für .jar Dateien . Dieser Mechanismus unterstützt sogar Handler nach Dateierweiterung. Eine weitere Anwendung ist Transparentes Ausführen von Programmen auf einer anderen Architektur mit QEMU .

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

PATH Suchmotivation

Eine wichtige Motivation für die Existenz von Shebangs ist wahrscheinlich die Tatsache, dass wir in Linux oft Befehle von PATH so wie:

basename-of-command

anstelle von:

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

Aber wie würde Linux ohne den Shebang-Mechanismus wissen, wie es die einzelnen Dateitypen starten soll?

Festcodierung der Erweiterung in Befehlen:

 basename-of-command.py

oder die Implementierung der PATH-Suche auf jedem Interpreter:

python basename-of-command

wäre eine Möglichkeit, aber das hat das große Problem, dass alles kaputt geht, wenn wir uns jemals dazu entschließen, den Befehl in eine andere Sprache umzuformulieren.

Shebangs lösen dieses Problem hervorragend.

Wichtigster Anwendungsfall von env : pyenv und andere Versionsverwalter

Ein wichtiger Anwendungsfall, warum Sie Folgendes verwenden sollten #!/usr/bin/env python statt nur /usr/bin/python i pyenv .

pyenv ermöglicht es Ihnen, mehrere Python-Versionen auf einem einzigen Rechner zu installieren, um andere Projekte ohne Virtualisierung besser reproduzieren zu können.

Dann verwaltet es die "aktuelle" Python-Version, indem es ihre Reihenfolge im PATH festlegt: z.B. wie hier gezeigt apt-get install für verschiedene Python-Versionen ein von pyenv verwalteter Python könnte sich befinden unter:

/home/ciro/.pyenv/shims/python

also bei weitem nicht so viel wie /usr/bin/python die bei einigen Systemen über update-alternatives Symlinks .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X