¿El shebang determina el shell que ejecuta el script?


84

Esta puede ser una pregunta tonta, pero aún la hago. Si he declarado un shebang

#!/bin/bash 

al principio de my_shell_script.sh, así que siempre tengo que invocar este script usando bash

[my@comp]$bash my_shell_script.sh

o puedo usar por ejemplo

[my@comp]$sh my_shell_script.sh

y mi script determina el shell en ejecución usando el shebang? ¿Sucede lo mismo con kshShell? Estoy usando AIX.


66
hay una pequeña confusión de tu parte: cuando haces "_some_shell some_script", comienza _some_shell y le pide que interprete some_script. Entonces no, si haces "sh my_shell_script.sh" no interpretará el shebang, sino que interpretará el script en sh. Para usar el shebang: chmod +x my_shell_script.sh ; /path/to/my_shell_script.sh # or ./my_shell_script.sh if you happen to be in its directory
Olivier Dulac

Respuestas:


117

El shebang #! es una instancia legible para humanos de un número mágico que consiste en la cadena de bytes 0x23 0x21, que es utilizada por la exec()familia de funciones para determinar si el archivo a ejecutar es un script o un binario. Cuando el shebang está presente, exec()ejecutará el ejecutable especificado después del shebang.

Tenga en cuenta que esto significa que si invoca un script especificando el intérprete en la línea de comando, como se hace en los dos casos dados en la pregunta, exec()ejecutará el intérprete especificado en la línea de comando, ni siquiera mirará el script.

Entonces, como otros han señalado, si desea exec()invocar al intérprete especificado en la línea shebang, el script debe tener el bit ejecutable establecido e invocado como ./my_shell_script.sh.

El comportamiento es fácil de demostrar con el siguiente script:

#!/bin/ksh
readlink /proc/$$/exe

Explicación:

  • #!/bin/kshdefine kshser el intérprete.

  • $$ contiene el PID del proceso actual.

  • /proc/pid/exe es un enlace simbólico al ejecutable del proceso (al menos en Linux; en AIX, /proc/$$/object/a.out es un enlace al ejecutable).

  • readlink generará el valor del enlace simbólico.

Ejemplo:

Nota : Estoy demostrando esto en Ubuntu, donde el shell predeterminado /bin/shes un enlace simbólico para guiar, es decir, /bin/dashy /bin/kshes un enlace simbólico /etc/alternatives/ksh, que a su vez es un enlace simbólico /bin/pdksh.

$ chmod +x getshell.sh
$ ./getshell.sh 
/bin/pdksh
$ bash getshell.sh 
/bin/bash
$ sh getshell.sh 
/bin/dash

gracias Thomas por esta respuesta. Supongamos que lanzamos el script como un proceso secundario desde Node.js o Java o lo que sea. ¿Podemos iniciar un proceso "exec" y luego exec ejecutará el script de shell? Pregunto porque estoy buscando respuestas a esta pregunta: stackoverflow.com/questions/41067872/…
Alexander Mills

1
@AlexanderMills Lo que se exec()menciona en esta respuesta es una llamada al sistema, el comando execes un shell incorporado, por lo que no puede invocar un exec programa desde Node.js o Java. Sin embargo, cualquier comando de shell invocado por, por ejemplo, Runtime.exec()en Java finalmente es procesado por la exec()llamada al sistema.
Thomas Nyman

Huh, sí, estoy familiarizado con la API de Java que acabas de mencionar, me pregunto si hay una manera de invocar la llamada exec () de nivel inferior desde Node.js de alguna manera
Alexander Mills

@AlexanderMills, me imagino child_process.{exec(),execFile(),spawn()}, todo se implementaría usando C exec()(a través process).
Thomas Nyman

10

Si lo hace Por cierto, no es una pregunta tonta. Una referencia para mi respuesta está aquí . Comenzar un script con #!

  • Se llama shebang o línea "bang".

  • No es más que el camino absoluto hacia el intérprete de Bash.

  • Consiste en un signo de número y un signo de exclamación (#!), Seguido de la ruta completa al intérprete como / bin / bash.

    Todos los scripts en Linux se ejecutan usando el intérprete especificado en una primera línea Casi todos los scripts de bash a menudo comienzan con #! / Bin / bash (suponiendo que Bash se haya instalado en / bin) Esto garantiza que Bash se utilizará para interpretar el script, incluso si se ejecuta bajo otro shell El shebang fue presentado por Dennis Ritchie entre la Versión 7 Unix y la 8 en los Laboratorios Bell. Luego también se agregó a la línea BSD en Berkeley.

Ignorar una línea de intérprete (shebang)

Si no especifica una línea de intérprete, el valor predeterminado suele ser / bin / sh. Pero, se recomienda que establezca #! / Bin / bash line.


3
Para elaborar, el kernel solo sabe cómo ejecutar archivos binarios enlazados estáticamente y dónde encontrar información del intérprete para otros (un campo especial en el binario o la línea shebang). Por lo general, ejecutar un script de shell significa seguir la línea shebang al shell y luego seguir el campo DT_INTERP en el binario del shell al vinculador dinámico.
Simon Richter

55
También tenga en cuenta que esto no se limita a los scripts de shell. Todos los archivos de script basados ​​en texto usan esto. Por ejemplo, #!/usr/bin/perl #!/usr/local/bin/python #!/usr/local/bin/rubyotra entrada común de shebang utilizada para admitir múltiples sistemas es usar env para localizar el intérprete que desea usar, como#!/usr/bin/env perl #!/usr/bin/env python
sambler

@sambler hablando env, ¿cuál debería preferirse realmente? Python y Perl a menudo usan env, mientras que en shellscripts, esto a menudo se omite y shebang apunta al shell en cuestión.
polemon

1
@polemon menos de lo que se prefiere y más en qué caminos varían. Los shells básicos están en la misma ruta en todos los sistemas. Las versiones actualizadas de perl y python se pueden instalar en diferentes ubicaciones en diferentes sistemas, por lo que usar env permite que el mismo shebang funcione siempre, por lo que env se usa más con scripts de perl y python que con scripts de shell.
Sambler

envencontrar un programa en $ PATH es un poco hack. No establece variables de entorno como su nombre lo indica. $ PATH podría ser un resultado diferente para diferentes usuarios. Pero ayuda a que los scripts se ejecuten sin modificaciones en los sistemas que colocan un intérprete perl razonable en algún lugar extraño.
John Mahowald

4

La execllamada al sistema del kernel de Linux comprende shebangs ( #!) de forma nativa

Cuando lo haces en bash:

./something

en Linux, esto llama a la execllamada del sistema con la ruta ./something.

Esta línea del núcleo se llama en el archivo pasado a exec: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_script.c#L25

if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))

Lee los primeros bytes del archivo y los compara con #!.

Si la comparación es verdadera, el núcleo de Linux analiza el resto de la línea, que realiza otra execllamada con la ruta /usr/bin/env pythony el archivo actual como primer argumento:

/usr/bin/env python /path/to/script.py

y esto funciona para cualquier lenguaje de secuencias de comandos que se use #como carácter de comentario.

Y sí, puedes hacer un ciclo infinito con:

printf '#!/a\n' | sudo tee /a
sudo chmod +x /a
/a

Bash reconoce el error:

-bash: /a: /a: bad interpreter: Too many levels of symbolic links

#! Simplemente resulta ser legible para los humanos, pero eso no es obligatorio.

Si el archivo comenzó con diferentes bytes, entonces la execllamada al sistema usaría un controlador diferente. El otro controlador incorporado más importante es para archivos ejecutables ELF: https://github.com/torvalds/linux/blob/v4.8/fs/binfmt_elf.c#L1305 que comprueba los bytes 7f 45 4c 46(que también son humanos legible para .ELF). Confirmemos eso leyendo los 4 primeros bytes de /bin/ls, que es un ejecutable ELF:

head -c 4 "$(which ls)" | hd 

salida:

00000000  7f 45 4c 46                                       |.ELF|
00000004                                                                 

Entonces, cuando el núcleo ve esos bytes, toma el archivo ELF, lo guarda en la memoria correctamente y comienza un nuevo proceso con él. Ver también: https://stackoverflow.com/questions/8352535/how-does-kernel-get-an-executable-binary-file-running-under-linux/31394861#31394861

Finalmente, puede agregar sus propios manejadores de shebang con el binfmt_miscmecanismo. Por ejemplo, puede agregar un controlador personalizado para .jararchivos . Este mecanismo incluso admite controladores por extensión de archivo. Otra aplicación es ejecutar de forma transparente ejecutables de una arquitectura diferente con QEMU .

Sin embargo, no creo que POSIX especifique shebangs: https://unix.stackexchange.com/a/346214/32558 , aunque sí lo menciona en las secciones de justificación y en la forma "si el sistema admite scripts ejecutables Puede pasar".


1
Ejecutar ./somethingdesde un shell no pasará la ruta completaexec , sino exactamente la ruta ingresada. ¿Puedes corregir esto en tu respuesta? Haga echo "$0"en su script y verá que este es el caso.
AndiDog

2

De hecho, si lo toma en consecuencia, el ejecutable anotado en la línea shebang, es solo un ejecutable. Tiene sentido usar algún intérprete de texto como ejecutable, pero no es necesario. Solo para aclaración y demostración, hice una prueba bastante inútil:

#!/bin/cat
useless text
more useless text
still more useless text

Nombrado el archivo test.txt y establecer el bit exectuable chmod u+x test.txt, a continuación, "llamada" que: ./test.txt. Como se esperaba, el contenido del archivo se emite. En este caso, el gato no ignora la línea shebang. Simplemente genera todas las líneas. Cualquier intérprete útil debería ser capaz de ignorar esta línea shebang. Para bash, perl y PHP, es simplemente una línea de comentarios. Entonces sí, estos ignoran la línea shebang.


-1

Por lo que obtuve, cada vez que un archivo tiene un conjunto de bits ejecutable y se invoca, el núcleo analiza el encabezado del archivo para determinar cómo proceder (hasta donde yo sé, puede agregar controladores personalizados para formatos de archivo personalizados a través de LKM). Si el archivo parece ser un archivo de texto con un #! combinación al principio, su ejecución se envía a otro ejecutable (generalmente una especie de shell), una ruta a la que se debe especificar directamente después de dicho shebang, en la misma línea. El núcleo luego procede a ejecutar el shell y pasa el archivo para que lo maneje.

En resumen, no importa con qué shell invoque el script: el núcleo enviará la ejecución al apropiado de cualquier manera.


44
Hay una marcada diferencia entre bash ./myscript.shy ./myscript.sh.
un CVn

¿Qué quieres decir con esta "marcada diferencia"?
jrara

3
@jrara Vea mi respuesta, la afirmación de que "no importa con qué shell invoque el script" simplemente no es cierta.
Thomas Nyman
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.