Ich bin in einer interessanten Situation, in der ich ein Python-Skript habe, das theoretisch von einer Vielzahl von Benutzern mit einer Vielzahl von Umgebungen (und PATHs) und auf einer Vielzahl von Linux-Systemen ausgeführt werden kann. Ich möchte, dass dieses Skript auf so vielen von ihnen wie möglich ohne künstliche Einschränkungen ausführbar ist. Hier sind einige bekannte Setups:
- Python 2.6 ist die System-Python-Version, daher existieren Python, Python2 und Python2.6 alle in / usr / bin (und sind äquivalent).
- Python 2.6 ist die System-Python-Version wie oben, aber Python 2.7 wird als Python2.7 daneben installiert.
- Python 2.4 ist die System-Python-Version, die mein Skript nicht unterstützt. In / usr / bin haben wir Python, Python2 und Python2.4, die äquivalent sind, und Python2.5, die das Skript unterstützt.
Ich möchte auf allen drei das gleiche ausführbare Python-Skript ausführen. Es wäre schön, wenn es zuerst versuchen würde, /usr/bin/python2.7 zu verwenden, wenn es existiert, dann auf /usr/bin/python2.6 zurückgreifen und dann auf /usr/bin/python2.5 zurückgreifen Einfach Fehler raus, wenn keiner von denen anwesend war. Ich bin jedoch nicht zu sehr damit beschäftigt, das neueste 2.x zu verwenden, solange es in der Lage ist, einen der richtigen Interpreten zu finden, falls vorhanden.
Meine erste Neigung war es, die Shebang-Linie zu ändern von:
#!/usr/bin/python
zu
#!/usr/bin/python2.[5-7]
da dies gut in bash funktioniert. Das Ausführen des Skripts bietet jedoch Folgendes:
/usr/bin/python2.[5-7]: bad interpreter: No such file or directory
Okay, also versuche ich folgendes, was auch in bash funktioniert:
#!/bin/bash -c /usr/bin/python2.[5-7]
Aber auch dies schlägt fehl mit:
/bin/bash: - : invalid option
Okay, natürlich könnte ich einfach ein separates Shell-Skript schreiben, das den richtigen Interpreter findet und das Python-Skript mit dem gefundenen Interpreter ausführt. Ich finde es einfach mühsam, zwei Dateien zu verteilen, von denen eine ausreichen sollte, solange der aktuellste Python 2-Interpreter installiert ist. Das explizite Aufrufen des Interpreters (z. B. $ python2.5 script.py
) ist keine Option. Es ist auch keine Option, sich darauf zu verlassen, dass der PATH des Benutzers auf eine bestimmte Weise eingerichtet wurde.
Bearbeiten:
Die Versionsprüfung innerhalb des Python-Skripts funktioniert nicht , da ich die "with" -Anweisung verwende, die ab Python 2.6 existiert (und in 2.5 mit verwendet werden kann from __future__ import with_statement
). Dies führt dazu, dass das Skript mit einem benutzerunfreundlichen SyntaxError sofort fehlschlägt, und verhindert, dass ich jemals die Möglichkeit habe, die Version zuerst zu überprüfen und einen entsprechenden Fehler zu melden.
Beispiel: (versuchen Sie dies mit einem Python-Interpreter unter 2.6)
#!/usr/bin/env python
import sys
print "You'll never see this!"
sys.exit()
with open('/dev/null', 'w') as out:
out.write('something')
import sys; sys.version_info()
jedoch überprüfen, ob der Benutzer die erforderliche Python-Version hat../script.py
würde das direkte Ausführen des Skripts (dh ) dazu führen, dass Python 2.4 ausgeführt wird, wodurch Ihr Code erkennt, dass es sich um die falsche Version handelt (und möglicherweise beendet wird). Aber es gibt ein perfektes Python2.5, das stattdessen als Interpreter hätte verwendet werden können!exec
Sie andernfalls einen Fehler aus.execve
). Die Argumente sind Zeichenfolgenliterale, keine Globen, keine regulären Ausdrücke. Das ist es. Selbst wenn das erste Argument "/ bin / bash" und die zweiten Optionen ("-c ...") sind, werden diese Optionen nicht von einer Shell analysiert. Sie werden unbehandelt an die ausführbare Bash-Datei übergeben, weshalb Sie diese Fehler erhalten. Außerdem funktioniert der Shebang nur, wenn er am Anfang steht. Ich fürchte, Sie haben hier kein Glück (es fehlt ein Skript, das einen Python-Interpreter findet und ihm ein HIER-Dokument zuführt, das wie ein schreckliches Durcheinander klingt).Antworten:
Ich bin kein Experte, aber ich glaube, Sie sollten nicht die genaue Python-Version angeben, die verwendet werden soll, und diese Auswahl dem System / Benutzer überlassen.
Außerdem sollten Sie diesen Pfad anstelle des Hardcodierungspfads für Python im Skript verwenden:
oder
Es wird von Python doc in allen Versionen empfohlen :
In verschiedenen Distributionen kann Python an verschiedenen Orten installiert sein, also
env
wird danach gesuchtPATH
. Es sollte in allen wichtigen Linux-Distributionen und in FreeBSD verfügbar sein.Das Skript sollte mit der Version von Python ausgeführt werden, die sich in Ihrem PATH befindet und die von Ihrer Distribution * ausgewählt wurde.
Wenn Ihr Skript mit allen Versionen von Python außer 2.4 kompatibel ist, sollten Sie nur überprüfen, ob es in Python 2.4 ausgeführt wird, und einige Informationen ausdrucken und beenden.
Mehr zu lesen
env
.Fußnote
* In Gentoo gibt es ein Tool namens
eselect
. Damit können Sie Standardversionen verschiedener Anwendungen (einschließlich Python) als Standard festlegen:quelle
Auf der Grundlage einiger Ideen aus einigen Kommentaren gelang es mir, einen wirklich hässlichen Hack zusammenzuschustern, der zu funktionieren scheint. Das Skript wird zu einem Bash-Skript, das ein Python-Skript umschließt und über ein "Here-Dokument" an einen Python-Interpreter weiterleitet.
Am Anfang:
Der Python-Code geht hier. Dann ganz am Ende:
Wenn der Benutzer das Skript ausführt, wird die neueste Python-Version zwischen 2.5 und 2.7 verwendet, um den Rest des Skripts als Here-Dokument zu interpretieren.
Eine Erklärung zu einigen Spielereien:
Das Triple-Quote-Zeug, das ich hinzugefügt habe, ermöglicht es auch, dasselbe Skript als Python-Modul zu importieren (das ich zu Testzwecken verwende). Beim Import von Python wird alles zwischen dem ersten und dem zweiten einfachen Anführungszeichen als Zeichenfolge auf Modulebene interpretiert, und das dritte einfache Anführungszeichen wird auskommentiert. Der Rest ist gewöhnliches Python.
Bei direkter Ausführung (jetzt als Bash-Skript) werden die ersten beiden einfachen Anführungszeichen zu einer leeren Zeichenfolge, und das dritte einfache Anführungszeichen bildet eine weitere Zeichenfolge mit dem vierten einfachen Anführungszeichen, das nur einen Doppelpunkt enthält. Diese Zeichenfolge wird von Bash als No-Op interpretiert. Alles andere ist die Bash-Syntax zum Verschieben der Python-Binärdateien in / usr / bin, zum Auswählen der letzten und zum Ausführen von exec, wobei der Rest der Datei als Here-Dokument übergeben wird. Das vorliegende Dokument beginnt mit einem dreifachen einfachen Anführungszeichen für Python, das nur ein Hash- / Pfund- / Oktothorpe-Zeichen enthält. Der Rest des Skripts wird dann als normal interpretiert, bis die Zeile '# EOF' das Here-Dokument beendet.
Ich finde das pervers, also hoffe ich, dass jemand eine bessere Lösung hat.
quelle
# ft=python
.Die Shebang-Linie kann nur einen festen Pfad zu einem Interpreter angeben. Es gibt den
#!/usr/bin/env
Trick, den Dolmetscher imPATH
nachzuschlagen, aber das war's. Wenn Sie mehr Raffinesse wünschen, müssen Sie Wrapper-Shell-Code schreiben.Die naheliegendste Lösung besteht darin, ein Wrapper-Skript zu schreiben. Rufen Sie das Python-Skript auf
foo.real
und erstellen Sie ein Wrapper-Skriptfoo
:Wenn Sie alles in eine Datei einfügen möchten, können Sie es häufig zu einem Polyglot machen , das mit einer
#!/bin/sh
Zeile beginnt (also von der Shell ausgeführt wird), aber auch ein gültiges Skript in einer anderen Sprache ist. Abhängig von der Sprache ist eine Mehrsprachigkeit möglicherweise nicht möglich (wenn dies beispielsweise#!
einen Syntaxfehler verursacht). In Python ist es nicht sehr schwierig.(Der gesamte Text zwischen
'''
und'''
ist eine Python-Zeichenfolge auf oberster Ebene, die keine Auswirkung hat. Bei der Shell ist die zweite Zeile''':'
nach dem Entfernen von Anführungszeichen der Befehl no-op:
.)quelle
# EOF
wie in dieser Antwort am Ende hinzugefügt werden muss . Ihr Ansatz ist im Grunde derselbe wie hier beschrieben .Da Ihre Anforderungen eine bekannte Liste von Binärdateien enthalten, können Sie dies in Python folgendermaßen tun. Es würde nach einer einstelligen Neben- / Hauptversion von Python nicht mehr funktionieren, aber ich sehe, dass dies in Kürze nicht mehr möglich ist.
Führt die höchste auf der Festplatte befindliche Version aus der geordneten, aufsteigenden Liste versionierter Pythons aus, wenn die in der Binärdatei markierte Version höher ist als die aktuelle Version von Python, die ausgeführt wird. Die "geordnete aufsteigende Liste von Versionen" ist das wichtige Bit für diesen Code.
Entschuldigung für meine kratzige Python
quelle
from __future__ import with_statement
, was das erste sein muss, was im Python-Skript enthalten ist. Vermutlich wissen Sie nicht, wie Sie diese Aktion ausführen können, wenn Sie den neuen Interpreter starten.with
s wie ein normaler Import zu verwenden ?. Funktioniert ein Extraif mypy == 25: from __future__ import with_statement
kurz vor "normalen Sachen"? Sie brauchen das if wahrscheinlich nicht, wenn Sie 2.4 nicht unterstützen.Sie können ein kleines Bash-Skript schreiben, das nach der verfügbaren ausführbaren Phython-Datei sucht und diese mit dem Skript als Parameter aufruft. Sie können dieses Skript dann zum Ziel der Shebang-Linie machen:
Und dieses Skript macht einfach (nach der Suche):
Ich war mir nicht sicher, ob der Kernel diese Skript-Indirektion akzeptieren würde, aber ich habe sie überprüft und sie funktioniert.
Bearbeiten 1
Um diese peinliche Unaufmerksamkeit endlich zu einem guten Vorschlag zu machen:
Es ist möglich, beide Skripte in einer Datei zu kombinieren. Sie schreiben das Python-Skript einfach als Here-Dokument im Bash-Skript (wenn Sie das Python-Skript ändern, müssen Sie die Skripte nur noch einmal zusammen kopieren). Entweder erstellen Sie eine temporäre Datei in z. B. / tmp oder Sie geben das Skript als Eingabe für den Interpreter ein (falls Python dies unterstützt, weiß ich nicht):
quelle
$(ls /usr/bin/python?.? | tail -n1 )
mir jedoch nicht gelungen, dies in einem Shebang geschickt zu verwenden.