Je suis dans une situation intéressante où j'ai un script Python qui peut théoriquement être exécuté par une variété d'utilisateurs avec une variété d'environnements (et de CHEMINS) et sur une variété de systèmes Linux. Je veux que ce script soit exécutable sur autant de ceux-ci que possible sans restrictions artificielles. Voici quelques configurations connues:
- Python 2.6 est la version système de Python, donc python, python2 et python2.6 existent tous dans / usr / bin (et sont équivalents).
- Python 2.6 est la version système de Python, comme ci-dessus, mais Python 2.7 est installé à côté d'elle en tant que python2.7.
- Python 2.4 est la version système de Python, que mon script ne prend pas en charge. Dans / usr / bin, nous avons python, python2 et python2.4 qui sont équivalents et python2.5, que le script prend en charge.
Je veux exécuter le même script python exécutable sur ces trois. Ce serait bien s'il essayait d'abord d'utiliser /usr/bin/python2.7, s'il existe, puis de revenir à /usr/bin/python2.6, puis de revenir à /usr/bin/python2.5, puis erreur simplement si aucun d'entre eux n'était présent. Je ne suis pas trop accroché à cela en utilisant la version 2.x la plus récente possible, tant qu'il est capable de trouver l'un des interprètes appropriés s'il est présent.
Ma première inclination a été de changer la ligne de shebang de:
#!/usr/bin/python
à
#!/usr/bin/python2.[5-7]
car cela fonctionne très bien en bash. Mais l'exécution du script donne:
/usr/bin/python2.[5-7]: bad interpreter: No such file or directory
D'accord, j'essaie donc ce qui suit, qui fonctionne également en bash:
#!/bin/bash -c /usr/bin/python2.[5-7]
Mais encore une fois, cela échoue avec:
/bin/bash: - : invalid option
Bien sûr, je pourrais simplement écrire un script shell séparé qui trouve l'interpréteur correct et exécute le script python à l'aide de l'interpréteur qu'il a trouvé. Je trouverais juste compliqué de distribuer deux fichiers où un devrait suffire tant qu'il est exécuté avec l'interpréteur python 2 le plus récent installé. Demander aux gens d'invoquer explicitement l'interpréteur (par exemple, $ python2.5 script.py
) n'est pas une option. S'appuyer sur le PATH de l'utilisateur en cours de configuration d'une certaine manière n'est pas non plus une option.
Éditer:
La vérification de version dans le script Python ne fonctionnera pas car j'utilise l'instruction "with" qui existe à partir de Python 2.6 (et peut être utilisée dans 2.5 avec from __future__ import with_statement
). Cela provoque l'échec immédiat du script avec une erreur de syntaxe peu conviviale et m'empêche d'avoir la possibilité de vérifier d'abord la version et d'émettre une erreur appropriée.
Exemple: (essayez ceci avec un interpréteur Python inférieur à 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()
pour vérifier si l'utilisateur a la version python requise../script.py
) Entraînerait l'exécution de python2.4, ce qui entraînerait votre code à détecter qu'il s'agissait de la mauvaise version (et à quitter, probablement). Mais il y a un très bon python2.5 qui aurait pu être utilisé comme interprète à la place!exec
si c'est le cas, sinon affichez une erreur.execve
). Les arguments sont des littéraux de chaîne, pas de globalisation, pas de regexps. C'est ça. Même si le premier argument est "/ bin / bash" et les secondes options ("-c ...") ces options ne sont pas analysées par un shell. Ils sont remis non traités à l'exécutable bash, c'est pourquoi vous obtenez ces erreurs. De plus, le shebang ne fonctionne que s'il est au début. Donc, vous n'avez pas de chance ici, je le crains (à court d'un script qui trouve un interprète python et le nourrit d'un document ICI, ce qui ressemble à un désordre horrible).Réponses:
Je ne suis pas expert, mais je pense que vous ne devriez pas spécifier la version exacte de python à utiliser et laisser ce choix au système / utilisateur.
Vous devriez également utiliser cela au lieu du chemin de codage en dur vers python dans le script:
ou
Il est recommandé par Python doc dans toutes les versions:
Dans diverses distributions, Python peut être installé à différents endroits, il
env
le recherchera donc dansPATH
. Il devrait être disponible dans toutes les principales distributions Linux et d'après ce que je vois dans FreeBSD.Le script doit être exécuté avec cette version de Python qui est dans votre PATH et qui est choisie par votre distribution *.
Si votre script est compatible avec toutes les versions de Python sauf 2.4, vous devez simplement vérifier à l'intérieur s'il est exécuté en Python 2.4 et imprimer quelques informations et quitter.
Plus à lire
env
.note de bas de page
* Dans Gentoo, il existe un outil appelé
eselect
. En l'utilisant, vous pouvez définir les versions par défaut de différentes applications (y compris Python) par défaut:la source
Sur la base de quelques idées de quelques commentaires, j'ai réussi à concocter un hack vraiment laid qui semble fonctionner. Le script devient un script bash enveloppe un script Python et le transmet à un interpréteur Python via un "document ici".
Au début:
Le code Python va ici. Puis à la toute fin:
Lorsque l'utilisateur exécute le script, la version Python la plus récente entre 2.5 et 2.7 est utilisée pour interpréter le reste du script comme un document ici.
Une explication sur certains manigances:
Le truc de guillemet triple que j'ai ajouté permet également d'importer ce même script en tant que module Python (que j'utilise à des fins de test). Lorsqu'il est importé par Python, tout ce qui se trouve entre le premier et le deuxième guillemet triple simple est interprété comme une chaîne de niveau module, et le troisième guillemet triple simple est mis en commentaire. Le reste est du Python ordinaire.
Lorsqu'il est exécuté directement (en tant que script bash maintenant), les deux premiers guillemets simples deviennent une chaîne vide et le troisième guillemet simple forme une autre chaîne avec le quatrième guillemet simple, ne contenant que deux points. Cette chaîne est interprétée par Bash comme un no-op. Tout le reste est la syntaxe Bash pour globaliser les binaires Python dans / usr / bin, sélectionner le dernier et exécuter exec, en passant le reste du fichier comme document ici. Le document ici commence par une citation triple-simple Python contenant uniquement un signe de hachage / livre / octothorpe. Le reste du script est alors interprété comme normal jusqu'à ce que la ligne lisant '# EOF' termine le document ici.
J'ai l'impression que c'est pervers, alors j'espère que quelqu'un a une meilleure solution.
la source
# ft=python
.La ligne shebang ne peut spécifier qu'un chemin fixe vers un interprète. Il y a l'
#!/usr/bin/env
astuce pour rechercher l'interprète dans lePATH
mais c'est tout. Si vous voulez plus de sophistication, vous devrez écrire du code shell wrapper.La solution la plus évidente consiste à écrire un script wrapper. Appelez le script python
foo.real
et créez un script wrapperfoo
:Si vous voulez tout mettre dans un fichier, vous pouvez souvent en faire un polyglotte qui commence par une
#!/bin/sh
ligne (donc sera exécuté par le shell) mais est également un script valide dans une autre langue. Selon la langue, un polyglotte peut être impossible (si cela#!
provoque une erreur de syntaxe, par exemple). En Python, ce n'est pas très difficile.(Le texte entier entre
'''
et'''
est une chaîne Python au niveau supérieur, ce qui n'a aucun effet. Pour le shell, la deuxième ligne est celle''':'
qui, après suppression des guillemets, est la commande no-op:
.)la source
# EOF
à la fin comme dans cette réponse . Votre approche est fondamentalement la même que celle décrite ici .Comme vos besoins indiquent une liste connue de binaires, vous pouvez le faire en Python avec ce qui suit. Cela ne fonctionnerait pas après une version mineure / majeure à un chiffre de Python, mais je ne vois pas cela se produire de si tôt.
Exécute la version la plus élevée située sur le disque à partir de la liste ordonnée et croissante de pythons versionnés, si la version balisée sur le binaire est supérieure à la version actuelle de python en cours d'exécution. La "liste ordonnée croissante des versions" étant l'élément le plus important pour ce code.
Toutes mes excuses pour mon python rugueux
la source
from __future__ import with_statement
, ce qui doit être la première chose dans le script Python. Je suppose que vous ne savez pas comment exécuter cette action lors du démarrage du nouvel interprète?with
s comme une importation normale?. Unif mypy == 25: from __future__ import with_statement
travail supplémentaire fonctionne-t-il juste avant les «choses normales»? Vous n'avez probablement pas besoin du if, si vous ne supportez pas 2.4.Vous pouvez écrire un petit script bash qui vérifie l'exécutable phython disponible et l'appelle avec le script comme paramètre. Vous pouvez ensuite faire de ce script la cible de la ligne shebang:
Et ce script fait simplement (après la recherche):
J'étais incertain si le noyau accepterait cette indirection de script mais j'ai vérifié et cela fonctionne.
Modifier 1
Pour faire de cette imperceptibilité embarrassante une bonne proposition enfin:
Il est possible de combiner les deux scripts dans un seul fichier. Vous écrivez simplement le script python en tant que document ici dans le script bash (si vous changez le script python, il vous suffit de recopier les scripts ensemble). Soit vous créez un fichier temporaire dans eg / tmp ou (si python le supporte, je ne sais pas) vous fournissez le script comme entrée à l'interpréteur:
la source
$(ls /usr/bin/python?.? | tail -n1 )
mais je n'ai pas réussi à l'utiliser intelligemment dans un shebang.