Comment utiliser les variables d'environnement dans mon shebang?

34

J'ai un script Python qui doit être exécuté avec une installation Python particulière. Y a-t-il un moyen de fabriquer un shebang pour qu'il fonctionne avec $FOO/bar/MyCustomPython?

eric.frederich
la source

Réponses:

29

La ligne de shebang est très limitée. Sous de nombreuses variantes unix (y compris Linux), vous ne pouvez avoir que deux mots: une commande et un seul argument. Il y a aussi souvent une limitation de longueur.

La solution générale consiste à écrire un petit wrapper shell. Nommez le script Python foo.py, mettez le script shell à côté foo.pyet appelez-le foo. Cette approche ne nécessite aucun en-tête particulier sur le script Python.

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

Une autre approche tentante consiste à écrire un script wrapper similaire à celui ci-dessus et à l'insérer #!/path/to/wrapper/scriptcomme ligne shebang dans le script Python. Cependant, la plupart des ordinateurs ne supportent pas l'enchaînement de scripts shebang, donc cela ne fonctionnera pas.

Si MyCustomPythonétait dans le $PATH, vous pouvez utiliser envpour le rechercher:

#!/usr/bin/env MyCustomPython
import 

Une autre approche consiste à faire en sorte que le script soit à la fois un script shell valide (qui charge le bon interpréteur Python sur lui-même) et un script valide dans le langage cible (ici Python). Cela nécessite que vous trouviez un moyen d'écrire un tel script bilingue pour votre langue cible. En Perl, cela s'appelle if $running_under_some_shell.

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

Voici un moyen d'obtenir le même effet en Python. Dans le shell, "true"c'est l' trueutilitaire qui ignore ses arguments (deux chaînes à caractère unique :et ') et renvoie une valeur true. En Python, "true"est une chaîne qui est vraie lorsqu'elle est interprétée comme un booléen. Il s'agit donc d'une ifinstruction toujours vraie et qui exécute un littéral de chaîne.

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

Le code Rosetta a de tels scripts bilingues dans plusieurs autres langues.

Gilles, arrête de faire le mal
la source
1
@Chris: tout est entre guillemets simples. S'il vous plaît expliquer…
Stéphane Gimenez
1
Tu es un sorcier sanglant ... C'est génial. J'ai utilisé cela pour modifier la PYTHONPATHvariable env afin de charger des modules à partir d'un emplacement non standard pour un script CGI sur un système auquel je n'avais pas accès root. Épargnant de vie. Merci encore.
wspurgin
12

Les lignes Shebang ne subissent pas de développement variable, vous ne pouvez donc pas utiliser $FOO/MyCustomPythoncar elles rechercheraient un exécutable nommé dollar-FOO -...

Une alternative consiste à faire pointer votre shebang sur un script shell en tant qu'interprète, qui peut ensuite utiliser des variables d'environnement pour localiser le correct et l'exécuter.

Exemple: créez un mypython.shscript dans /usr/local/bin(ou tout autre répertoire de votre $PATH), avec le contenu suivant:

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

alors vous pouvez utiliser cette ligne shebang pour faire exécuter un script Python MyCustomPythonvia mypython.sh:

#!/usr/bin/env mypython.sh
Riccardo Murri
la source
6
Utilisez $python, non pas $PYTHON- par convention, nous capitalisons les variables d’environnement (PAGER, EDITOR, SHELL ... $PYTHONdans votre script n’est pas une variable d’environnement) et les variables d’enveloppe internes (BASH_VERSION, RANDOM, ...). Tous les autres noms de variables doivent contenir au moins une lettre minuscule. Cette convention évite de passer outre aux variables environnementales et internes accidentellement. De plus, n'utilisez pas d'extensions pour vos scripts. Les scripts définissent de nouvelles commandes que vous pouvez exécuter et les commandes ne reçoivent généralement pas d'extensions.
Chris Down
4

Vous pouvez soit utiliser le chemin absolu de l’installation python personnalisée, soit le placer dans votre $PATHet l’utiliser #!/usr/bin/env [command]. Sinon, écrivez un wrapper pour cela et utilisez-le execpour remplacer l'image de processus, à titre d'exemple:

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

Tout d'abord, déterminez en quoi votre version de Python est différente de la version standard de Python déjà installée (par exemple, un module supplémentaire), puis au début du programme 'switch', comment s'appelle Python:

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

Cela devrait permettre au programme d'être appelé par toute autre instance de Python. Je fais quelque chose de similaire pour une instance avec la bibliothèque SQL intégrée qui ne peut pas être importée en tant que module dans le python du système.

Arcege
la source
0

Si vous pouvez remplacer $FOOdans $FOO/bar/MyCustomPythondes options de chemin en béton, vous pouvez dire à la envligne tralala où rechercher en mettant directement votre version Python personnalisé une coutume PATHen elle.

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

Edit : semble fonctionner uniquement sans guillemets autour de l'attribution de valeur PATH:

#!/usr/bin/env PATH=/path1/to/MyCustomPython:/path2/to/MyCustomPython python
chan
la source
5
Attention, cela ne fonctionne pas sur toutes les variantes Unix (par exemple, pas sur Linux). Et vous supprimez tous les répertoires existants du chemin, ce qui risque de poser des problèmes en bout de ligne.
Gilles 'SO- arrête d'être méchant'
1
Mieux développer PATHcomme ceci:PATH=$PATH:/path/to/whatever...
Bengt