¿Cómo puedo usar variables de entorno en mi shebang?

34

Tengo un script de Python que se debe ejecutar con una instalación de Python en particular. ¿Hay alguna manera de crear un shebang para que funcione $FOO/bar/MyCustomPython?

eric.frederich
fuente

Respuestas:

29

La línea shebang es muy limitada. En muchas variantes de Unix (incluido Linux), solo puede tener dos palabras: un comando y un único argumento. También a menudo hay una limitación de longitud.

La solución general es escribir un pequeño envoltorio de shell. Asigne un nombre al script de Python foo.py, coloque el script de shell al lado foo.pyy llámelo foo. Este enfoque no requiere ningún encabezado particular en el script de Python.

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

Otro enfoque tentador es escribir una secuencia de comandos de envoltura como la anterior y ponerla #!/path/to/wrapper/scriptcomo la línea shebang en la secuencia de comandos de Python. Sin embargo, la mayoría de las unidades no admiten el encadenamiento de scripts shebang, por lo que esto no funcionará.

Si MyCustomPythonestaba en el $PATH, podría usar envpara buscarlo:

#!/usr/bin/env MyCustomPython
import 

Otro enfoque es organizar que el script sea tanto un script de shell válido (que carga el intérprete de Python correcto en sí mismo) como un script válido en el lenguaje de destino (aquí Python). Esto requiere que encuentre una manera de escribir una secuencia de comandos de doble idioma para su idioma de destino. En Perl, esto se conoce como if $running_under_some_shell.

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

Aquí hay una forma de lograr el mismo efecto en Python. En el shell, "true"está la trueutilidad, que ignora sus argumentos (dos cadenas de un solo carácter :y ') y devuelve un valor verdadero. En Python, "true"es una cadena que es verdadera cuando se interpreta como un booleano, por lo que esta es una ifinstrucción que siempre es verdadera y ejecuta un literal de cadena.

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

El código de Rosetta tiene dichos scripts en dos idiomas en varios otros idiomas.

Gilles 'SO- deja de ser malvado'
fuente
1
@ Chris: todo está dentro de comillas simples. Por favor explique ...
Stéphane Gimenez
1
Eres un mago sangriento ... Esto es genial. Utilicé esto para editar la PYTHONPATHvariable env para cargar módulos desde una ubicación no estándar para un script CGI en un sistema al que no tenía acceso root. Salvavidas Gracias de nuevo.
wspurgin
12

Las líneas Shebang no experimentan una expansión variable, por lo que no puede usar, $FOO/MyCustomPythonya que buscaría un ejecutable llamado dollar-FOO -...

Una alternativa es hacer que su shebang apunte a un script de shell como intérprete, y este script de shell puede usar variables de entorno para localizar el correcto y ejecutarlo.

Ejemplo: cree un mypython.shscript en /usr/local/bin(o cualquier otro directorio en su $PATH), con el siguiente contenido:

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

entonces puede usar esta línea shebang para ejecutar un script Python a MyCustomPythontravés de mypython.sh:

#!/usr/bin/env mypython.sh
Riccardo Murri
fuente
66
Uso $python, no $PYTHON- por convención, capitalizamos las variables de entorno (PAGER, EDITOR, SHELL ... $PYTHONen su script no es una variable de entorno) y las variables de shell internas (BASH_VERSION, RANDOM, ...). Todos los demás nombres de variables deben contener al menos una letra minúscula. Esta convención evita anular accidentalmente variables ambientales e internas. Además, no use extensiones para sus scripts. Las secuencias de comandos definen nuevos comandos que puede ejecutar, y los comandos generalmente no tienen extensiones.
Chris Down
4

Puede usar la ruta absoluta a la instalación personalizada de Python, o puede ponerla en su $PATHy usar #!/usr/bin/env [command]. De lo contrario, escriba un contenedor para ello y úselo execpara reemplazar la imagen del proceso, como ejemplo:

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

Primero, determine cómo su versión de Python es diferente de la versión estándar de Python ya instalada (por ejemplo, un módulo adicional) y luego, al inicio del programa, “cambie” cómo se llama a 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

Esto debería permitir que cualquier otra instancia de Python llame al programa. Hago algo similar para una instancia con la biblioteca sql incorporada que no se puede importar como módulo en la python del sistema.

Arcege
fuente
0

Si se puede sustituir $FOOen $FOO/bar/MyCustomPythonla opción de trazado de hormigón, se puede decir la envlínea de tinglado donde para buscar su versión personalizada de Python mediante el establecimiento de una costumbre directamente PATHen ella.

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

Editar : parece funcionar solo sin comillas alrededor de la asignación del valor PATH:

#!/usr/bin/env PATH=/path1/to/MyCustomPython:/path2/to/MyCustomPython python
chan
fuente
55
Tenga en cuenta que esto no funciona en todas las variantes de Unix (por ejemplo, no en Linux). Y está eliminando todos los directorios existentes de la ruta, lo que probablemente cause problemas en el futuro.
Gilles 'SO- deja de ser malvado'
1
Mejor expandirse PATHasí:PATH=$PATH:/path/to/whatever...
Bengt