2010-06-09 14 views
37

En un script, debe incluir un #! en la primera línea seguido de la ruta al programa que ejecutará el script (por ejemplo, sh, perl).¿Cómo funciona el #! Shebang trabajo?

Hasta donde yo sé, el carácter # denota el comienzo de un comentario y se supone que el programa que ejecuta el script lo ignora. Parece que esta primera línea es, en algún momento, leída por algo para que el programa adecuado pueda ejecutar la secuencia de comandos.

¿Alguien podría arrojar más luz sobre el funcionamiento del #!?

Tengo mucha curiosidad por esto, así que cuanto más profunda sea la respuesta, mejor.

+0

He hecho una buena educación sobre este tema en el hilo comp.lang.shell [PostScript programas ejecutables] (http://groups.google. com/group/comp.unix.shell/browse_thread/thread/e7a3306342c01847/ec5741ed3278408a? q = ejecutable + postscript + programas # ec5741ed3278408a) Al escribir un simple programa C para manipular la línea de comandos, pude crear scripts ejecutables para un lenguaje que normalmente no hace eso. –

Respuesta

30

Lectura recomendada:

cargador de programa del kernel de UNIX es responsable de hacer esto. Cuando se llama al exec(), le pide al kernel que cargue el programa desde el archivo en su argumento. A continuación, comprobará los primeros 16 bits del archivo para ver qué formato ejecutable tiene. Si encuentra que estos bits son #!, utilizará el resto de la primera línea del archivo para encontrar el programa que debería iniciarse, y proporciona el nombre del archivo que intentaba iniciar (el script) como último argumento para el programa de intérprete

El intérprete se ejecuta normalmente y trata el #! como una línea de comentario.

+0

Lo mejor es que el archivo puede ser cualquier cosa, no necesariamente un programa, siempre que el programa invocado pueda tolerar el shebang. –

+0

@KevinPanko: ¿Son siempre exactamente 16 bits los que verifica el cargador de programas del kernel? ¿Qué sucedería si '#!' Estuvieran precedidos por una BOM UTF-8 o UTF-16? – stakx

+1

@stakx Sí, esto fue inventado antes de Unicode y no se ha modificado desde entonces. http://unicode.org/faq/utf_bom.html#bom5 –

8

historia corta: El shebang (#!) línea es leído por la carcasa (por ejemplo sh, bash, etc.) del sistema operativo programa cargador. Si bien formalmente se ve como un comentario, el hecho de que sean los dos primeros bytes de un archivo marca todo el archivo como un archivo de texto y como un script. El script se pasará al ejecutable mencionado en la primera línea después del shebang. Voilà!


historia ligeramente más largo: Imagine que tiene la secuencia de comandos, foo.sh, con el (x) bit ejecutable. Este archivo contiene, p. lo siguiente:

#!/bin/sh 

# some script commands follow...: 
# *snip* 

Ahora, en su shell, escriba:

> ./foo.sh 

Editar: Lea también los comentarios a continuación después o antes de leer el siguiente! Como resultado, estaba equivocado. Aparentemente, no es el shell el que pasa el script al intérprete de destino, sino el sistema operativo (kernel) en sí mismo.

Recuerde que escribir esta dentro del proceso de la shell (Vamos a suponer que este es el programa /bin/sh). Por lo tanto, esa entrada tendrá que ser procesada por ese programa. Interpreta esta línea como un comando, ya que descubre que lo primero que se ingresa en la línea es el nombre de un archivo que realmente existe y que tiene los bits ejecutables establecidos.

/bin/sh luego comienza a leer el contenido del archivo y descubre el shebang (#!) justo al comienzo del archivo. Para el shell, este es un token ("número mágico") por el cual sabe que el archivo contiene un script.

Ahora, ¿cómo sabe en qué lenguaje de programación está escrita la secuencia de comandos? Después de todo, puede ejecutar scripts Bash, scripts Perl, scripts de Python, ... Todo lo que el shell sabe hasta ahora es que está mirando un archivo de script (que no es un archivo binario, sino un archivo de texto). Por lo tanto, lee la siguiente entrada hasta el primer salto de línea (que dará como resultado /bin/sh, compare con el anterior). Este es el intérprete al que se le pasará el script para su ejecución. (En este caso particular, el intérprete de destino es el intérprete de comandos, por lo que no tiene que invocar un nuevo intérprete de comandos para el guión, simplemente procesa el resto del archivo de guión).

Si el guión estaba destinado por ejemplo/bin/perl, todo lo que el intérprete de Perl (opcionalmente) tendría que hacer es ver si la línea shebang realmente menciona el intérprete Perl. De lo contrario, el intérprete de Perl sabría que no puede ejecutar este script. Si de hecho el intérprete de Perl se menciona en la línea de shebang, lee el resto del archivo de guión y lo ejecuta.

+4

Los dos primeros bytes de un ejecutable son el número mágico que indica cómo se debe ejecutar; para scripts interpretados, los primeros dos bytes corresponden convenientemente a los caracteres ASCII '#!' – friedo

+7

No es el shell el que mira esos dos bytes, es el sistema (cargador de programa), ¿sí? Lo mismo sucede si ejecuta el script desde dentro de un shell o no. – Cascabel

+4

El shebang no es manejado por el shell, es manejado por el sistema operativo en sí. –

1

El núcleo de Linux exec llamada al sistema utiliza los bytes iniciales #! para identificar el tipo de archivo

Cuando haces en bash:

./something 

en Linux, esto requiere la llamada exec sistema con el pleno ruta al something.

Esta línea se llama en el kernel en el fichero 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, entonces el resto de la línea es analizada por el núcleo de Linux, lo que hace otra llamada exec con el camino de /usr/bin/env python y archivo actual como primer argumento:

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

y esto funciona para cualquier lenguaje de scripting que use # como un caracter de comentario.

Sí, se puede hacer un bucle infinito con:

#!/a 

y un archivo ejecutable en la ruta /a

#! es legible por humanos, pero eso no es necesario.

Si el archivo se inició con diferentes bytes, la llamada al sistema exec utilizará un controlador diferente.El otro controlador incorporado más importante es para los 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 es legible para el humano .ELF), que lee el archivo elf, lo coloca en la memoria correctamente y comienza un nuevo proceso con eso. Vea también: How does kernel get an executable binary file running under linux?

Además, puede agregar sus propios manejadores de shebang el mecanismo binfmt_misc. Por ejemplo, puede agregar un controlador personalizado para los archivos .jar: Running a JAR file without directly calling `java` Este mecanismo incluso admite manejadores por extensión de archivo. http://stackoverflow.com/questions/3009192/how-does-the-shebang-work/40938907#40938907

No creo POSIX especifica shebangs sin embargo: https://unix.stackexchange.com/a/346214/32558

Cuestiones relacionadas