¿Hay alguna forma de elegir dinámicamente el intérprete que ejecuta un script? Tengo un script que estoy ejecutando en dos sistemas diferentes, y el intérprete que quiero usar está ubicado en diferentes ubicaciones en los dos sistemas. Lo que tengo que hacer es cambiar la línea hashbang cada vez que cambio. Me gustaría hacer algo que sea el equivalente lógico de esto (me doy cuenta de que esta construcción exacta es imposible):
if running on system A:
#!/path/to/python/on/systemA
elif running on system B:
#!/path/on/systemB
#Rest of script goes here
O incluso mejor sería esto, de modo que intente usar el primer intérprete, y si no lo encuentra, usa el segundo:
try:
#!/path/to/python/on/systemA
except:
#!path/on/systemB
#Rest of script goes here
Obviamente, en su lugar, puedo ejecutarlo como
/path/to/python/on/systemA myscript.py
o
/path/on/systemB myscript.py
dependiendo de dónde estoy, pero en realidad tengo un script de contenedor que se inicia myscript.py
, por lo que me gustaría especificar la ruta al intérprete de Python mediante programación en lugar de hacerlo a mano.
if
condición no es una opción para usted? como,if something; then /bin/sh restofscript.sh elif...
Respuestas:
No, eso no funcionará. Los dos caracteres
#!
deben ser absolutamente los primeros dos caracteres en el archivo (¿cómo especificaría qué interpreta la declaración if de todos modos?). Esto constituye el "número mágico" que laexec()
familia de funciones detecta cuando determina si un archivo que está a punto de ejecutar es un script (que necesita un intérprete) o un archivo binario (que no lo hace).El formato de la línea shebang es bastante estricto. Necesita tener una ruta absoluta hacia un intérprete y, como máximo, un argumento para ello.
Lo que puedes hacer es usar
env
:Ahora, el camino
env
es generalmente/usr/bin/env
, pero técnicamente eso no es garantía.Esto le permite ajustar la
PATH
variable de entorno en cada sistema de manera queinterpreter
(ya seabash
,python
operl
, o lo que tengas) se encuentra.Una desventaja de este enfoque es que será imposible pasar un argumento de forma portátil al intérprete.
Esto significa que
y
Es poco probable que funcione en algunos sistemas.
Otro enfoque obvio es usar las herramientas automáticas GNU (o algún sistema de plantillas más simple) para encontrar el intérprete y colocar la ruta correcta en el archivo en un
./configure
paso, que se ejecutará al instalar el script en cada sistema.También se podría recurrir a ejecutar el script con un intérprete explícito, pero eso es obviamente lo que está tratando de evitar:
fuente
#!
tiene que venir al principio, ya que no es el shell que procesa esa línea. Me preguntaba si hay una forma de poner lógica dentro de la línea hashbang que sería equivalente a if / else. También esperaba evitar perder el tiempo con miPATH
pero creo que esas son mis únicas opciones.#!/usr/bin/awk
, puede proporcionar exactamente un argumento, como#!/usr/bin/awk -f
. Si el binario al que está apuntando esenv
, el argumento es el binario que está pidiendoenv
que busque, como en#!/usr/bin/env awk
./usr/bin/env
con el argumento únicoawk -f
.foo.awk
con la línea hashbang#!/usr/bin/env awk -f
y./foo.awk
luego lo llama con , en Linux, lo queenv
ve son los dos parámetrosawk -f
y./foo.awk
. En realidad va buscando/usr/bin/awk -f
(etc.) con un espacio.Siempre puede hacer un script de contenedor para encontrar el intérprete correcto para el programa real:
Guarde el contenedor en los usuarios
PATH
comoprogram
y deje a un lado el programa real o con otro nombre.Solía
#!/bin/bash
en el hashbang debido a laflags
matriz. Si no necesita almacenar un número variable de banderas o algo así y puede prescindir de él, el script debería funcionar de manera portátil#!/bin/sh
.fuente
exec "$interpreter" "${flags[@]}" "$script" "$@"
también solía mantener el árbol del proceso más limpio. También propaga el código de salida.exec
.#!/bin/sh
sería mejor en lugar de#!/bin/bash
? Incluso si/bin/sh
es un enlace simbólico a un shell diferente, debería existir en la mayoría (si no todos) los sistemas * nix, además de forzar al autor del script a crear un script portátil en lugar de caer en bashismos.flags
es una característica no estándar, pero es lo suficientemente útil para almacenar un número variable de banderas, por lo que decidí mantenerla.script=/what/ever; something && exec this "$script" "$@"; exec that "$script" -x -y "$@"
. También puede agregar la comprobación de errores para fallas de ejecución.También puedes escribir un políglota (combina dos idiomas). / bin / sh está garantizado para existir.
Esto tiene la desventaja del código feo y quizás algunos
/bin/sh
s podrían confundirse. Pero se puede usar cuandoenv
no existe o existe en otro lugar que no sea / usr / bin / env. También se puede usar si desea hacer una selección bastante elegante.La primera parte del script determina qué intérprete usar cuando se ejecuta con / bin / sh como intérprete, pero se ignora cuando lo ejecuta el intérprete correcto. Úselo
exec
para evitar que el shell se ejecute más que la primera parte.Ejemplo de Python:
fuente
exec "$interpreter" "$0" "$@"
llevar el nombre del guión al intérprete real. (Y luego espero que nadie haya mentido al configurar$0
)#!
, Scala ignora todo hasta una coincidencia!#
; esto le permite poner código de script complejo arbitrariamente en un lenguaje arbitrario allí, y luegoexec
el motor de ejecución Scala con el script.Prefiero las respuestas de Kusalananda e ilkkachu, pero aquí hay una respuesta alternativa que hace más directamente lo que pedía la pregunta, simplemente porque se hizo.
Tenga en cuenta que solo puede hacer esto cuando el intérprete permite escribir código en el primer argumento. Aquí,
-e
y todo después de esto se toma literalmente como 1 argumento para ruby. Por lo que puedo decir, no puedes usar bash para el código shebang, porquebash -c
requiere que el código esté en un argumento separado.Intenté hacer lo mismo con Python para el código shebang:
pero resulta demasiado largo y Linux (al menos en mi máquina) trunca el shebang a 127 caracteres.
exec
Disculpe el uso de insertar nuevas líneas, ya que python no permite try-excepts oimport
s sin nuevas líneas.No estoy seguro de cuán portátil es, y no lo haría en un código destinado a ser distribuido. Sin embargo, es factible. Tal vez alguien lo encuentre útil para la depuración rápida y sucia o algo así.
fuente
Si bien esto no selecciona el intérprete dentro del script de shell (lo selecciona por máquina) es una alternativa más fácil si tiene acceso administrativo a todas las máquinas en las que está intentando ejecutar el script.
Cree un enlace simbólico (o un enlace duro si lo desea) para apuntar a la ruta del intérprete deseada. Por ejemplo, en mi sistema, Perl y Python están en / usr / bin:
crearía un enlace simbólico para permitir que el hashbang se resuelva para / bin / perl, etc. Esto preserva la capacidad de pasar parámetros a los scripts también.
fuente
Me enfrenté a un problema similar como este hoy (
python3
señalando una versión de Python que era demasiado antigua en un sistema), y se me ocurrió un enfoque que es un poco diferente de los discutidos aquí: use la versión "incorrecta" de Python para arrancar en el "correcto". La limitación es que algunas versiones de python deben ser accesibles de manera confiable, pero eso generalmente se puede lograr por ejemplo#!/usr/bin/env python3
.Entonces, lo que hago es comenzar mi script con:
Lo que esto hace es:
fuente