Me pregunto si hay una forma general de pasar múltiples opciones a un ejecutable a través de la línea shebang ( #!
).
Yo uso NixOS, y la primera parte del shebang en cualquier script que escribo suele ser /usr/bin/env
. El problema que encuentro es que todo lo que viene después es interpretado como un único archivo o directorio por el sistema.
Supongamos, por ejemplo, que quiero escribir un script para que se ejecute bash
en modo posix. La forma ingenua de escribir el shebang sería:
#!/usr/bin/env bash --posix
pero intentar ejecutar el script resultante produce el siguiente error:
/usr/bin/env: ‘bash --posix’: No such file or directory
Soy consciente de esta publicación , pero me preguntaba si había una solución más general y más limpia.
EDITAR : Sé que para los guiones Guile , hay una manera de lograr lo que quiero, documentado en la Sección 4.3.4 del manual:
#!/usr/bin/env sh
exec guile -l fact -e '(@ (fac) main)' -s "$0" "$@"
!#
El truco, aquí, es que la segunda línea (comenzando con exec
) se interpreta como código sh
pero, al estar en el bloque #!
... !#
, como un comentario, y por lo tanto ignorada, por el intérprete Guile.
¿No sería posible generalizar este método a cualquier intérprete?
Segunda edición : después de jugar un poco, parece que, para los intérpretes que pueden leer sus comentarios stdin
, el siguiente método funcionaría:
#!/usr/bin/env sh
sed '1,2d' "$0" | bash --verbose --posix /dev/stdin; exit;
Sin embargo, probablemente no sea óptimo, ya que el sh
proceso dura hasta que el intérprete haya terminado su trabajo. Cualquier comentario o sugerencia sería apreciada.
Respuestas:
No existe una solución general, al menos no si necesita admitir Linux, porque el núcleo de Linux trata todo lo que sigue a la primera "palabra" en la línea shebang como un argumento único .
No estoy seguro de cuáles son las restricciones de NixOS, pero normalmente solo escribiría su shebang como
o, cuando sea posible, configure las opciones en el script :
Alternativamente, puede hacer que el script se reinicie con la invocación de shell apropiada:
Este enfoque se puede generalizar a otros idiomas, siempre y cuando encuentre una manera de ignorar el primer par de líneas (que son interpretadas por el shell).
GNU
coreutils
'env
proporciona una solución alternativa desde la versión 8.30, consulte la respuesta de Unode para más detalles. (Esto está disponible en Debian 10 y posterior, RHEL 8 y posterior, Ubuntu 19.04 y posterior, etc.)fuente
Aunque no es exactamente portátil, a partir de coreutils 8.30 y de acuerdo con su documentación , podrá utilizar:
Así dado:
conseguirás:
y en caso de que tengas curiosidad
showargs
es:fuente
env
donde-S
se agregó en 2005. Ver lists.gnu.org/r/coreutils/2018-04/msg00011.htmlshowargs
: pastebin.com/q9m6xr8H y pastebin.com/gS8AQ5WA (one-liner)env
incluye el suyoshowargs
: la opción -v, por ejemplo#!/usr/bin/env -vS --option1 --option2 ...
El estándar POSIX es muy breve al describir
#!
:De la sección de justificación de la documentación de la
exec()
familia de interfaces del sistema :Desde el sección de Introducción de Shell :
Esto básicamente significa que cualquier implementación (el Unix que está utilizando) es libre de hacer los detalles del análisis de la línea shebang como lo desee.
Algunos Unices, como macOS (no puede probar ATM), dividirán los argumentos dados al intérprete en la línea shebang en argumentos separados, mientras que Linux y la mayoría de los otros Unices darán los argumentos como una sola opción para el intérprete.
Por lo tanto, no es prudente confiar en que la línea shebang pueda tomar más de un argumento.
Vea también la sección de Portabilidad del artículo de Shebang en Wikipedia .
Una solución fácil, que se puede generalizar a cualquier utilidad o lenguaje, es crear un script de envoltura que ejecute el script real con los argumentos de línea de comando apropiados:
No creo que intente personalmente hacer que se vuelva a ejecutar , ya que eso se siente algo frágil.
fuente
El shebang se describe en
execve
(2) la página man de la siguiente manera:Se aceptan dos espacios en esta sintaxis:
Tenga en cuenta que no utilicé el plural al hablar de un argumento opcional, ni la sintaxis anterior usa
[optional-arg ...]
, ya que puede proporcionar a lo sumo un solo argumento .En lo que respecta a las secuencias de comandos de shell, puede utilizar el
set
comando incorporado cerca del comienzo de su secuencia de comandos que le permitirá establecer los parámetros de los intérpretes, proporcionando el mismo resultado que si usara argumentos de línea de comandos.En tu caso:
Desde el indicador Bash, verifique la salida de
help set
para obtener todas las opciones disponibles.fuente
En Linux, el shebang no es muy flexible; de acuerdo con las respuestas múltiples (la respuesta de Stephen Kitt y la de Jörg W Mittag ), no hay una forma designada para pasar múltiples argumentos en una línea shebang.
No estoy seguro de si será de utilidad para alguien, pero he escrito un breve guión para implementar la función que falta. Ver https://gist.github.com/loxaxs/7cbe84aed1c38cf18f70d8427bed1efa .
También es posible escribir soluciones alternativas integradas. A continuación, presento cuatro soluciones independientes del lenguaje aplicadas al mismo script de prueba y el resultado que imprime cada una. Supongo que el script es ejecutable y está en
/tmp/shebang
.Envolviendo su script en un bash heredoc dentro de la sustitución del proceso
Hasta donde yo sé, esta es la forma más confiable de hacer un lenguaje independiente. Permite pasar argumentos y preserva stdin. El inconveniente es que el intérprete no conoce la ubicación (real) del archivo que lee.
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
Impresiones de llamadas :Tenga en cuenta que la sustitución del proceso produce un archivo especial. Esto puede no adaptarse a todos los ejecutables. Por ejemplo, se
#!/usr/bin/less
queja:/dev/fd/63 is not a regular file (use -f to see it)
No sé si es posible tener heredoc dentro de la sustitución del proceso en el tablero.
Envolviendo su script en un simple heredoc
Más corto y simple, pero no podrá acceder
stdin
desde su script y requiere que el intérprete pueda leer y ejecutar un scriptstdin
.echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
Impresiones de llamadas :Usa una
system()
llamada awk pero sin argumentosPasa correctamente el nombre del archivo ejecutado, pero su script no recibirá los argumentos que le dé. Tenga en cuenta que awk es el único idioma que conozco cuyo intérprete está instalado en Linux de forma predeterminada y lee sus instrucciones desde la línea de comandos de forma predeterminada.
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
Impresiones de llamadas :Use awk 4.1+
system()
llamada , siempre que sus argumentos no contengan espaciosAgradable, pero solo si está seguro de que su script no se llamará con argumentos que contengan espacios. Como puede ver, sus argumentos que contienen espacios se dividirían, a menos que se escapen los espacios.
echo -e 'aa\nbb' | /tmp/shebang 'arg1' 'arg2 contains spaces' 'arg3\ uses\ \\escapes\\'
Impresiones de llamadas :Para las versiones awk inferiores a 4.1, deberá utilizar la concatenación de cadenas dentro de un bucle for, consulte la función de ejemplo https://www.gnu.org/software/gawk/manual/html_node/Join-Function.html .
fuente
$variable
o`command`
sustituir:exec python3 -O <(cat <<'EOWRAPPER'
Un truco para usar
LD_LIBRARY_PATH
con python en la línea#!
(shebang) que no depende de nada más que el shell y funciona como un regalo:Como se explica en otra parte de esta página, algunos shells como
sh
pueden tomar un script en su entrada estándar.El script que damos
sh
intenta ejecutar el comando''''
que se simplifica a''
(la cadena vacía)sh
y, por supuesto, no puede ejecutarlo ya que no hay un''
comando, por lo que normalmente se muestraline 2: command not found
en el descriptor de error estándar, pero redirigimos este mensaje2>/dev/null
al agujero negro más cercano porque sería desordenado y confuso para el usuario dejarlosh
mostrar.Luego procedemos al comando que nos interesa:
exec
que reemplaza el proceso actual de shell por lo que sigue, en nuestro caso:/usr/bin/env python
con los parámetros adecuados:"$0"
para que Python sepa qué script debe abrir e interpretar, y también establecersys.argv[0]
"$@"
para configurar pythonsys.argv[1:]
a los argumentos pasados en la línea de comandos del script.Y también pedimos
env
establecer laLD_LIBRARY_PATH
variable de entorno, que es el único punto del hack.El comando de shell termina en el comentario que comienza
#
para que el shell ignore las comillas triples finales'''
.sh
luego se reemplaza por una nueva instancia brillante del intérprete de python que abre y lee el script de origen de python dado como primer argumento (el"$0"
).Python abre el archivo y salta la primera línea de la fuente gracias al
-x
argumento. Nota: también funciona sin-x
porque para Python un shebang es solo un comentario .Python luego interpreta la segunda línea como la cadena de documentación para el archivo de módulo actual, por lo que si necesita una cadena de documentación de módulo válida, simplemente configure lo
__doc__
primero en su programa de Python como en el ejemplo anterior.fuente
''''exec ...
debería hacer el trabajo. Tenga en cuenta que no hay espacio antes de exec o lo hará buscar el comando vacío. Desea empalmar el vacío en el primer argumento para que así$0
seaexec
.Encontré una solución bastante estúpida al buscar un ejecutable que exceptúa un script como argumento único:
fuente