Wie kann ich Umgebungsvariablen in meinem Shebang verwenden?

34

Ich habe ein Python-Skript, das mit einer bestimmten Python-Installation ausgeführt werden muss. Gibt es eine Möglichkeit, einen Shebang zu basteln, mit dem er läuft $FOO/bar/MyCustomPython?

eric.frederich
quelle

Antworten:

29

Die Shebang-Linie ist sehr begrenzt. Unter vielen Unix-Varianten (einschließlich Linux) können Sie nur zwei Wörter haben: einen Befehl und ein einziges Argument. Oft gibt es auch eine Längenbeschränkung.

Die allgemeine Lösung besteht darin, einen kleinen Shell-Wrapper zu schreiben. Nennen Sie das Python-Skript foo.py, setzen Sie das Shell-Skript neben foo.pyund rufen Sie es auf foo. Für diesen Ansatz ist kein bestimmter Header im Python-Skript erforderlich.

#!/bin/sh
exec "$FOO/bar/MyCustomPython" "$0.py" "$@"

Ein weiterer verlockender Ansatz besteht darin, ein Wrapper-Skript wie das oben beschriebene zu schreiben und es #!/path/to/wrapper/scriptals Shebang-Zeile in das Python-Skript einzufügen. Die meisten Unices unterstützen jedoch keine Verkettung von Shebang-Skripten, sodass dies nicht funktioniert.

Wenn in MyCustomPythonwar $PATH, können Sie es verwenden env, um nachzuschlagen:

#!/usr/bin/env MyCustomPython
import 

Ein weiterer Ansatz besteht darin, dafür zu sorgen, dass das Skript sowohl ein gültiges Shell-Skript (das den richtigen Python-Interpreter auf sich selbst lädt) als auch ein gültiges Skript in der Zielsprache (hier Python) ist. Dies setzt voraus, dass Sie einen Weg finden, ein solches zweisprachiges Skript für Ihre Zielsprache zu schreiben. In Perl ist dies bekannt als if $running_under_some_shell.

#!/bin/sh
eval 'exec "$FOO/bar/MyCustomPerl" -wS $0 ${1+"$@"}'
    if $running_under_some_shell;
use 

Hier ist eine Möglichkeit, den gleichen Effekt in Python zu erzielen. In der Shell "true"befindet sich das trueDienstprogramm, das seine Argumente (zwei Zeichenfolgen :und ') ignoriert und einen wahren Wert zurückgibt. Ist in Python "true"eine Zeichenfolge, die wahr ist, wenn sie als Boolescher Wert interpretiert wird. Dies ist also eine ifAnweisung, die immer wahr ist und ein Zeichenfolgenliteral ausführt.

#!/bin/sh
if "true" : '''\'
then
exec "$FOO/bar/MyCustomPython" "$0" "$@"
exit 127
fi
'''
import 

Rosetta-Code verfügt über solche zweisprachigen Skripte in mehreren anderen Sprachen.

Gilles 'SO - hör auf böse zu sein'
quelle
1
@ Chris: alles in einfachen Anführungszeichen. Bitte erklären Sie ...
Stéphane Gimenez
1
Du bist ein verdammter Zauberer ... Das ist großartig. Ich habe damit die PYTHONPATHVariable env bearbeitet , um Module von einem nicht standardmäßigen Speicherort für ein CGI-Skript auf einem System zu laden, auf das ich keinen Root-Zugriff hatte. Lebensretter. Danke noch einmal.
wspurgin
12

Shebang-Zeilen werden nicht variabel erweitert. Sie können sie daher nicht verwenden, $FOO/MyCustomPythonda sie nach einer ausführbaren Datei mit dem Namen dollar-FOO -... suchen würden.

Alternativ können Sie Ihren shebang-Punkt auf ein Shell-Skript als Interpreter setzen. Dieses Shell-Skript kann dann Umgebungsvariablen verwenden, um das richtige zu finden und es auszuführen.

Beispiel: Erstellen Sie ein mypython.shSkript in /usr/local/bin(oder einem anderen Verzeichnis in Ihrem $PATH) mit folgendem Inhalt:

#! /bin/sh
PYTHON="$FOO/bar/MyCustomPython"
exec "$PYTHON" "$@"

Dann können Sie diese Shebang-Zeile verwenden, um ein Python-Skript auszuführen MyCustomPythonüber mypython.sh:

#!/usr/bin/env mypython.sh
Riccardo Murri
quelle
6
Verwendung $python, nicht $PYTHON- vereinbarungs profitieren wir von Umgebungsvariablen (PAGER, REDAKTION, SHELL ... $PYTHONin Ihrem Skript ist nicht eine Umgebungsvariable) und interne Shell - Variablen (BASH_VERSION, RANDOM, ...). Alle anderen Variablennamen sollten mindestens einen Kleinbuchstaben enthalten. Diese Konvention vermeidet das versehentliche Überschreiben von Umgebungsvariablen und internen Variablen. Verwenden Sie auch keine Erweiterungen für Ihre Skripte. Skripte definieren neue Befehle, die Sie ausführen können, und Befehle erhalten im Allgemeinen keine Erweiterungen.
Chris Down
4

Sie können entweder den absoluten Pfad zur benutzerdefinierten Python-Installation verwenden oder ihn in your $PATHand use einfügen #!/usr/bin/env [command]. Schreiben Sie andernfalls einen Wrapper dafür und execersetzen Sie das Prozessabbild anhand des folgenden Beispiels:

#!/bin/bash
exec "$ENV/python" "$@"
Chris Down
quelle
3

Stellen Sie zunächst fest, inwiefern sich Ihre Python-Version von der bereits installierten Standard-Python-Version unterscheidet (z. B. ein zusätzliches Modul), und ändern Sie dann beim Starten des Programms den Python-Namen:

#!/usr/bin/python
import os
import sys
try:
    import MyCustomModule
except ImportError:
    # only need to use expandvar if you know the string below has an envvar
    custprog = os.path.expandvar("$FOO/bar/MyCustomPython")
    os.execv(custprog, sys.argv) # call alternate python with the same arguments
    raise SystemExit('could not call %s' % custprog)
# if we get here, then we have the correct python interpreter

Dadurch sollte das Programm von jeder anderen Instanz von Python aufgerufen werden können. Ich mache etwas Ähnliches für eine Instanz mit integrierter SQL-Bibliothek, die nicht als Modul in den Python des Systems importiert werden kann.

Arcege
quelle
0

Wenn Sie ersetzen können $FOOin $FOO/bar/MyCustomPythonmit Betonweg Optionen können Sie die sagen envshebang Linie , wo Ihre benutzerdefinierte Python - Version zu sehen , indem direkt eine benutzerdefinierte Einstellung PATHin ihm.

#!/usr/bin/env PATH="/path1/to/MyCustomPython:/path2/to/MyCustomPython" python

Bearbeiten : Scheint nur ohne Anführungszeichen um die PATH-Wertzuweisung zu funktionieren:

#!/usr/bin/env PATH=/path1/to/MyCustomPython:/path2/to/MyCustomPython python
Chan
quelle
5
Beachten Sie, dass dies nicht bei allen Unix-Varianten funktioniert (z. B. nicht unter Linux). Und Sie entfernen alle vorhandenen Verzeichnisse aus dem Pfad, was später wahrscheinlich zu Problemen führen wird.
Gilles 'SO- hör auf böse zu sein'
1
Besser so erweitern PATH:PATH=$PATH:/path/to/whatever...
Bengt