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
sh
de 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 essh
, esto no es viable.Puedes llevar las cosas aún más lejos en esa línea si puedes confiar en
bash
estar presente: un script cuya primera línea esexec /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 tener
tail
omí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 python
en $PATH
lugar 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/bin
o /opt
lo 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 env
invoca 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 perlrun
pá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/sh
script, eval
se ejecuta perl -x -wS
en su script y pasa sus argumentos.
Cuando Perl lo ejecuta, está eval
controlado 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 sh
comandos 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:
Utilice
\$shell_var
el archivo para actuar como una "variable" para definir el shell que desee.Dado que Dash no es compatible
function f { echo hello world; }
sintaxis, por lo que produjo un errorfunction: not found
, pero cambiar el shell a bash en\$shell_var
el archivo no generará más errores.La ruta completa del archivo (o la ruta relativa si ./hole.sh se ejecuta en /tmp) pasará como
$@
al\$shell_var
archivo y/tmp/hole.sh
se ejecutará dentro de\$shell_var
.