¿Puedo utilizar un contenido variable como shebang?

¿Puedo utilizar un contenido variable como shebang?

Tengo un script de shell en el que quiero agregar un shebang. Dada una variable definida de la siguiente manera:

SHEBANG="#!/bin/sh"

Mi pregunta es si puedo usar esa variable en otro script como este:

$SHEBANG
# other stuff...

Respuesta1

No. La línea shebang es procesada por su kernel o biblioteca del sistema* y no procesa variables de shell.

  • Si quisiera utilizar una variable de entorno como intérprete por algún motivo, podría crear un ejecutable de "trampolín" (¡no un script!) que reapareciera $SHEBANG "$@". #!En este caso, deberías omitir el de la variable; #!solo es útil cuando se trata literalmente de los dos primeros bytes de un archivo.

  • Otra opción es hacer uso deel hecho de que los scripts sin líneas shebang (generalmente) se ejecutarán como scripts de shell con cualquiera shde los dos shells de llamada. Si hay una sintaxis de comentario para su objetivo shebang que se superpone útilmente con la sintaxis del shell, puede hacer que funcione. Esta estrategia es o solía ser común con los intérpretes Lisp. En el caso de su ejemplo, donde el intérprete también es sh, esto no es viable.

  • Puedes llevar las cosas aún más lejos en esa línea si puedes confiar en bashestar presente: un script cuya primera línea es

    exec /bin/bash -c 'exec "$SHEBANG" <(tail -n +2 "$0")' "$0" "$@"
    

    funcionará para la mayoría de los intérpretes; Bash se utiliza como paso intermedio para quesustitución de procesosse puede utilizar para tenertailomítenos la primera línea, para que no terminemos en un bucle infinito. Algunos intérpretes requieren que el archivo sea buscable y no aceptarán una canalización como esta.


Vale la pena considerar si esto es realmente algo que deseas hacer. Aparte de cualquier preocupación por la seguridad, simplemente no es muy útil: pocos scripts pueden tener sus intérpretes cambiados de esa manera, y donde sí es posible, es casi seguro que estaría mejor con una lógica más restringida que generara la correcta para la situación. (quizás un script de shell en el medio, que llama al intérprete con la ruta al script real como argumento).


* No siempre; en determinadas circunstancias, algunos shells lo leerán ellos mismos, pero aún así no realizarán una expansión variable.

Respuesta2

No sé si este es el caso de uso que tienes en mente, pero una cosapoderhacer es

#!/usr/bin/env python

por lo que su secuencia de comandos usa el primero pythonen $PATHlugar de necesitar codificar /usr/bin/python, lo que podría no funcionar bien en algún sistema donde esté la versión "buena" de Python /usr/local/bino /optlo que sea.

¿Por qué es mejor usar "#!/usr/bin/env NAME" en lugar de "#!/path/to/NAME" como mi shebang?


Esto no crea un bucle infinito porque el intérprete de Python (que envinvoca el archivo) simplemente trata la #!línea como un comentario. Este es el objetivo del #!diseño, porque muchos idiomas lo utilizan #como carácter de comentario.

Para extender esto,podrías escribir un programa políglotaque inicialmente se ejecuta bajo #!/bin/sh, pero luego descubre qué intérprete usar realmente y lo invoca en el mismo script.

Resulta que ya hayuna página web con trucos de shebang multilíneapara muchos idiomas, incluidos los lenguajes compilados (el script envía parte de sí mismo a un compilador y ejecuta la salida).


La perlrunpágina de manual ofrece este ejemplo como alternativa a #!/usr/bin/env perl:

    #!/bin/sh
    #! -*-perl-*-
    eval 'exec perl -x -wS $0 ${1+"$@"}'
        if 0;

    your perl script goes here

Cuando se ejecuta como un /bin/shscript, evalse ejecuta perl -x -wSen su script y pasa sus argumentos.

Cuando Perl lo ejecuta, está evalcontrolado por if 0;en la siguiente línea, por lo que nunca se ejecuta. A diferencia de sh, las declaraciones de Perl no terminan con una nueva línea.

Podrías hacer esto más complejo, usando "$SHEBANG"en lugar de perl, o tal vez incluso ejecutando múltiples shcomandos para seleccionar un intérprete de Perl para usar.

Respuesta3

Creo que esto es lo que quieres:

xb@dnxb:/tmp$ cat /tmp/hole.sh
#!/usr/bin/$shell_var
echo 1
readlink "/proc/$$/exe"
echo 2
function f { echo hello world; } 
echo 3 
xb@dnxb:/tmp$ chmod +x /tmp/hole.sh
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var 
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var 
#!/bin/bash
/bin/dash -- "$@"
xb@dnxb:/tmp$ sudo chmod +x /usr/bin/\$shell_var
xb@dnxb:/tmp$ ./hole.sh
1
/bin/dash
2
./hole.sh: 5: ./hole.sh: function: not found
3
xb@dnxb:/tmp$ sudo vi /usr/bin/\$shell_var 
xb@dnxb:/tmp$ cat /usr/bin/\$shell_var 
#!/bin/bash
/bin/bash -- "$@"
xb@dnxb:/tmp$ ./hole.sh 
1
/bin/bash
2
3
xb@dnxb:/tmp$ 

Explicación:

  1. Utilice \$shell_varel archivo para actuar como una "variable" para definir el shell que desee.

  2. readlink "/proc/$$/exe"imprimirá el shell actual.

  3. Dado que Dash no es compatiblefunction f { echo hello world; }sintaxis, por lo que produjo un error function: not found, pero cambiar el shell a bash en \$shell_varel archivo no generará más errores.

  4. La ruta completa del archivo (o la ruta relativa si ./hole.sh se ejecuta en /tmp) pasará como $@al \$shell_vararchivo y /tmp/hole.shse ejecutará dentro de \$shell_var.

información relacionada