Estoy en una situación interesante en la que tengo un script de Python que teóricamente puede ser ejecutado por una variedad de usuarios con una variedad de entornos (y PATHs) y en una variedad de sistemas Linux. Quiero que este script sea ejecutable en la mayor cantidad posible sin restricciones artificiales. Aquí hay algunas configuraciones conocidas:
- Python 2.6 es la versión de Python del sistema, por lo que python, python2 y python2.6 existen en / usr / bin (y son equivalentes).
- Python 2.6 es la versión de Python del sistema, como se indicó anteriormente, pero Python 2.7 está instalado junto a ella como python2.7.
- Python 2.4 es la versión de Python del sistema, que mi script no admite. En / usr / bin tenemos python, python2 y python2.4 que son equivalentes, y python2.5, que admite el script.
Quiero ejecutar el mismo script ejecutable de Python en estos tres. Sería bueno si intentara usar /usr/bin/python2.7 primero, si existe, luego volver a /usr/bin/python2.6, luego volver a /usr/bin/python2.5, luego simplemente error si ninguno de esos estaba presente. Sin embargo, no estoy demasiado obsesionado con el 2.x más reciente posible, siempre que pueda encontrar uno de los intérpretes correctos si está presente.
Mi primera inclinación fue cambiar la línea shebang de:
#!/usr/bin/python
a
#!/usr/bin/python2.[5-7]
ya que esto funciona bien en bash. Pero ejecutar el script da:
/usr/bin/python2.[5-7]: bad interpreter: No such file or directory
Bien, entonces intento lo siguiente, que también funciona en bash:
#!/bin/bash -c /usr/bin/python2.[5-7]
Pero de nuevo, esto falla con:
/bin/bash: - : invalid option
De acuerdo, obviamente podría escribir un script de shell separado que encuentre el intérprete correcto y ejecute el script de Python usando el intérprete que encuentre. Simplemente me resultaría una molestia distribuir dos archivos donde uno debería ser suficiente siempre que se ejecute con el intérprete de Python 2 más actualizado instalado. Pedirle a la gente que invoque al intérprete explícitamente (p. Ej. $ python2.5 script.py
) No es una opción. Confiar en que la RUTA del usuario se configure de cierta manera tampoco es una opción.
Editar:
La verificación de versiones dentro del script Python no funcionará ya que estoy usando la instrucción "con" que existe a partir de Python 2.6 (y se puede usar en 2.5 con from __future__ import with_statement
). Esto hace que el script falle inmediatamente con un SyntaxError hostil para el usuario, y me impide tener la oportunidad de verificar primero la versión y emitir un error apropiado.
Ejemplo: (intente esto con un intérprete de Python menor que 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()
para verificar si el usuario tiene la versión de Python requerida../script.py
) causaría que python2.4 lo ejecute, lo que haría que su código detecte que se trata de una versión incorrecta (y que se cierre, presumiblemente). ¡Pero hay un python2.5 perfectamente bueno que podría haber sido utilizado como intérprete!exec
, de ser así, imprima un error.execve
). Los argumentos son literales de cadena, sin globalización, sin expresiones regulares. Eso es. Incluso si el primer argumento es "/ bin / bash" y las segundas opciones ("-c ...") esas opciones no son analizadas por un shell. Se entregan sin tratar al ejecutable de bash, por lo que obtienes esos errores. Además, el shebang solo funciona si está al principio. Entonces, no tienes suerte aquí, me temo (a falta de un script que encuentre un intérprete de Python y lo alimente con un documento AQUÍ, que suena como un desastre horrible).Respuestas:
No soy experto, pero creo que no debe especificar la versión exacta de Python para usar y dejar esa opción al sistema / usuario.
También debe usar eso en lugar de la ruta de codificación fija a Python en el script:
o
Se recomienda por Python doc en todas las versiones:
En varias distribuciones, Python puede instalarse en diferentes lugares, por
env
lo que lo buscará enPATH
. Debería estar disponible en todas las principales distribuciones de Linux y de lo que veo en FreeBSD.El script debe ejecutarse con esa versión de Python que está en su RUTA y que es elegida por su distribución *.
Si su secuencia de comandos es compatible con todas las versiones de Python, excepto 2.4, debe verificar dentro de ella si se ejecuta en Python 2.4 e imprimir información y salir.
Más para leer
env
.Nota
* En Gentoo hay una herramienta llamada
eselect
. Al usarlo, puede establecer versiones predeterminadas de diferentes aplicaciones (incluida Python) como predeterminadas:fuente
Basándome en algunas ideas de algunos comentarios, logré armar un truco realmente feo que parece funcionar. El script se convierte en un script bash que envuelve un script de Python y lo pasa a un intérprete de Python a través de un "documento aquí".
Al principio:
El código de Python va aquí. Luego al final:
Cuando el usuario ejecuta el script, la versión más reciente de Python entre 2.5 y 2.7 se usa para interpretar el resto del script como un documento aquí.
Una explicación sobre algunas travesuras:
Las cosas de comillas triples que he agregado también permiten que este mismo script se importe como un módulo de Python (que uso para fines de prueba). Cuando Python lo importa, todo lo que se encuentra entre la primera y la segunda comillas triples se interpreta como una cadena de nivel de módulo, y la tercera comilla triple se comenta. El resto es Python ordinario.
Cuando se ejecuta directamente (como un script bash ahora), las dos primeras comillas simples se convierten en una cadena vacía, y la tercera comilla simple forma otra cadena con la cuarta comilla simple, que contiene solo dos puntos. Bash interpreta esta cadena como un no-op. Todo lo demás es sintaxis de Bash para bloquear los binarios de Python en / usr / bin, seleccionar el último y ejecutar exec, pasando el resto del archivo como un documento aquí. El documento aquí comienza con una comilla triple triple de Python que contiene solo un signo hash / pound / octothorpe. El resto del script se interpreta como normal hasta que la línea que dice '# EOF' finaliza el documento aquí.
Siento que esto es perverso, así que espero que alguien tenga una mejor solución.
fuente
# ft=python
.La línea shebang solo puede especificar una ruta fija a un intérprete. Existe el
#!/usr/bin/env
truco para buscar el intérprete en elPATH
pero eso es todo. Si quieres más sofisticación, deberás escribir un código de shell envoltorio.La solución más obvia es escribir un script de envoltura. Llame al script de python
foo.real
y cree un script de envolturafoo
:Si desea poner todo en un archivo, a menudo puede convertirlo en un políglota que comienza con una
#!/bin/sh
línea (por lo que será ejecutado por el shell) pero también es un script válido en otro idioma. Dependiendo del idioma, un políglota puede ser imposible (si#!
causa un error de sintaxis, por ejemplo). En Python, no es muy difícil.(Todo el texto entre
'''
y'''
es una cadena de Python en el nivel superior, que no tiene ningún efecto. Para el shell, la segunda línea es la''':'
que después de eliminar las comillas es el comando no-op:
.)fuente
# EOF
al final como en esta respuesta . Su enfoque básicamente es el mismo que se describe aquí .Como sus requisitos establecen una lista conocida de binarios, puede hacerlo en Python con lo siguiente. No funcionaría más allá de una versión menor / mayor de un solo dígito de Python, pero no veo que eso suceda pronto.
Ejecuta la versión más alta ubicada en el disco desde la lista ordenada y creciente de pitones versionadas, si la versión etiquetada en el binario es más alta que la versión actual de Python en ejecución. La "lista creciente de versiones ordenadas" es la parte importante de este código.
Disculpas por mi pitón rasposo
fuente
from __future__ import with_statement
, que debe ser lo primero en el script de Python. ¿Supongo que no sabes una forma de realizar esa acción al iniciar el nuevo intérprete?with
s como una importación normal? ¿Unif mypy == 25: from __future__ import with_statement
trabajo extra justo antes de 'cosas normales'? Probablemente no necesite el if, si no es compatible con 2.4.Puede escribir un pequeño script bash que verifique el ejecutable phython disponible y lo llame con el script como parámetro. Luego puede hacer que este script sea el objetivo de la línea shebang:
Y este script simplemente hace (después de la búsqueda):
No estaba seguro de si el núcleo aceptaría esta secuencia de comandos indirectamente, pero lo comprobé y funciona.
Editar 1
Para hacer de esta vergonzosa falta de percepción una buena propuesta finalmente:
Es posible combinar ambos scripts en un archivo. Simplemente escriba la secuencia de comandos de Python como un documento aquí en la secuencia de comandos bash (si cambia la secuencia de comandos de Python, solo necesita copiar las secuencias de comandos juntas de nuevo). O crea un archivo temporal en, por ejemplo, / tmp o (si Python lo admite, no lo sé), proporciona el script como entrada para el intérprete:
fuente
$(ls /usr/bin/python?.? | tail -n1 )
pero no logré usarlo de manera inteligente en un shebang.