¿Cómo funciona este shebang que comienza con un guión doble (-)?

14

He encontrado el siguiente tipo de shebang en la página de RosettaCode:

--() { :; }; exec db2 -txf "$0"

Funciona para Db2, y algo similar para Postgres. Sin embargo, no entiendo toda la línea.

Sé que el guión doble es un comentario en SQL, y después de eso llama al ejecutable Db2 con algunos parámetros que pasan el archivo como archivo. Pero, ¿qué pasa con el paréntesis, los corchetes, el colon y el punto y coma, y ​​cómo puede reemplazar un verdadero shebang #! ?

https://rosettacode.org/wiki/Multiline_shebang#PostgreSQL

AngocA
fuente

Respuestas:

18

Relacionado: ¿Qué intérprete de shell ejecuta un script sin shebang?

El script no tiene shebang / hashbang / #!line, simplemente porque no tiene un doble guión #!.

Sin embargo, el script será ejecutado por un shell (ver preguntas y respuestas vinculadas anteriormente), y en ese shell, si -es un carácter válido en el nombre de una función, la línea declara una función de shell llamada --que no hace nada (bueno, se ejecuta :, que no hace nada ) y que nunca se llama.

La función, en la notación de líneas múltiples más común (solo para que sea más obvio cómo se ve, ya que su nombre extraño oscurece el hecho de que en realidad es una función):

-- () {
  :
}

El único propósito de la definición de la función es tener una línea que sea válida en un script de shell y al mismo tiempo un comando SQL válido (un comentario). Este tipo de código se llama políglota .

Después de declarar la función de shell falsa, el script, cuando es ejecutado por un intérprete de script de shell, se usa execpara reemplazar el shell actual con el proceso resultante de la ejecución db2 -txf "$0", que sería lo mismo que usar db2 -txfen la ruta de acceso del script desde la línea de comandos.

Este truco probablemente no funcionaría de manera confiable en sistemas donde dashu otros ashshells basados yash, el shell Bourne, ksh88o ksh93se usan como /bin/sh, ya que estos shell no aceptan funciones cuyo nombre contenga guiones.

También relacionado:


Supongo que lo siguiente también funcionaría (no probado realmente):

--() { exec db2 -txf "$0"; }; --
Kusalananda
fuente
@ilkkachu ¿Mejor ahora?
Kusalananda
1
¡Oh si! Y gracias por recordarme cómo se llama ese tipo de cosas. :)
ilkkachu
6

Como ya dijo @Kusalananda, ese truco está roto y no funcionará en todos los proyectiles.

Aquí está mi opinión para hacerlo de forma portátil:

--/.. 2>/dev/null; exec db2 -txf "$0"

El primer comando debería fallar incluso si --existe un archivo / directorio nombrado en el directorio actual y cualquier error será cerrado por el 2>/dev/null; el shell continuará con el segundo comando, el exec.

Tio billy
fuente
Todavía no es realmente portátil. No es un script válido y todavía confía en el shell de llamada para evitar el hecho de que el núcleo se negará a ejecutar el script y volverá ENOEXECsi lo intenta. Intenta ejecutar el script debajo stracepara ver a qué me refiero.
kasperd
@kasperd, aún debe ser portátil, se supone que el shell debe ejecutar el script como un script de shell si exec()no funciona en él. "Si la función execl () falla debido a un error equivalente al error [ENOEXEC], el shell ejecutará un comando equivalente a tener un shell invocado con el nombre del comando como su primer operando, ..." (ver pubs.opengroup .org / onlinepubs / 9699919799.2018edition / utilities / ... )
ilkkachu
@ilkkachu Pero los scripts no siempre se ejecutan desde un shell. Si intenta usar el script en cualquier otro contexto donde un ejecutable funcionaría, fallará. Además, los shells no están de acuerdo con qué intérprete usar. Por lo tanto, su script ahora se comportará de manera diferente o fallará por completo según el contexto desde el que se llama.
kasperd
@kasperd, bueno, claro, no funcionará si lo haces exec()directamente desde otra cosa que no sea un shell. Pero, ¿cuál sería ese caso? Es posible que desee ejecutar el script crono algo similar, pero creo que de todos modos ejecuta todo a través de un shell, e incluso si no, es fácil de explicar db2 -txf /path/to/scripten ese caso, ya que solo necesita hacerlo una vez. Tener el trabajo abreviado es sobre todo útil en un shell interactivo. Pero claro, un script de contenedor separado podría ser más robusto.
ilkkachu
1
@kasperd No te molestaré con documentos y estándares; ¡solo inténtalo! echo 'int main(int c,char**a){execvp(a[1],a+1);}' | cc -include unistd.h -xc -; echo echo yeah > a.sh; chmod 755 a.sh; ./a.out ./a.sh; PATH=`pwd` ./a.out a.sh
Tío Billy