¿El intérprete lee #! / Bin / sh?


66

En basho sh, supongo que todo lo que comienza #es un comentario .

Pero en los bashguiones escribimos:

#!/bin/bash

Y en los scripts de Python, hay:

#!/bin/python

¿Esto significa que #por sí mismo es un comentario mientras #!que no lo es?


1
Y una vez que comience a mirar los perfiles de Apparmor, verá #include. Allí también, el #no se entiende como un comentario.

44
@ vasa1 Pero el punto clave que a menudo no es especialmente interesante acerca de las líneas Hashbang al comienzo de scripts de shell es que ellos son los comentarios .
Eliah Kagan el

Respuestas:


100

La #!línea se usa antes de ejecutar el script, luego se ignora cuando se ejecuta el script.

Estás preguntando cuál es la diferencia entre una línea shebang y un comentario ordinario.

Una línea que comienza #!es tanto un comentario como cualquier otra línea que comienza con #. Esto es cierto si #!es la primera línea del archivo o en cualquier otro lugar. #!/bin/sh tiene un efecto , pero el intérprete no lo lee .

#no es un comentario en todos los lenguajes de programación, pero, como saben, es un comentario en los shells de estilo Bourne que incluyen shy bash(así como la mayoría de los shells que no son de estilo Bourne, como csh). También es un comentario en Python . Y es un comentario en una variedad de archivos de configuración que en realidad no son scripts (como /etc/fstab).

Supongamos que un script de shell comienza con #!/bin/sh. Ese es un comentario, y el intérprete (el shell) ignora todo en la línea después del #personaje.

El propósito de una #!línea no es proporcionar información al intérprete. El propósito de la #!línea es decirle al sistema operativo (o cualquier proceso que inicie el intérprete) qué usar como intérprete .

  • Si invoca el script como un archivo ejecutable, por ejemplo, al ejecutar ./script.sh, el sistema consulta la primera línea para ver si comienza con #!, seguido de cero o más espacios, seguido de un comando. Si lo hace, ejecuta ese comando con el nombre del script como argumento. En este ejemplo, se ejecuta /bin/sh script.sh(o, técnicamente /bin/sh ./script.sh).

  • Si invoca el script llamando explícitamente al intérprete, la #!línea nunca se consulta. Entonces, si corres sh script.sh, la primera línea no tiene efecto. Si script2.shla primera línea es #!/usr/games/nibbles, la ejecución sh script2.shno intentará abrir el script en nibbles(pero lo ./script2.shhará).

Notarás que en ninguno de los casos la extensión del script ( .sh), si tiene una, afecta la forma en que se ejecuta. En un sistema tipo Unix, esto normalmente no afecta la forma en que se ejecuta el script. En algunos otros sistemas, como Windows, el sistema #!puede ignorar por completo la línea shebang, y la extensión puede determinar qué ejecuta los scripts. (Esto no significa que necesite dar extensiones a sus scripts, pero es una de las razones por las que si lo hace, deberían ser correctas).

#!fue elegido para cumplir este propósito precisamente porque # comienza un comentario. La #!línea es para el sistema, no para el intérprete, y debe ser ignorada por el intérprete.

Línea Shebang para secuencias de comandos Bash

Usted (originalmente) dijo que usaba #!/bin/shpara los bashscripts. Solo debe hacer eso si la secuencia de comandos no requiere ninguna de bashlas extensiones: shnecesita poder ejecutar la secuencia de comandos. shno siempre es un enlace simbólico a bash. A menudo, incluso en todos los sistemas Debian y Ubuntu remotos recientes , shes un enlace simbólico a dash.

Línea Shebang para scripts Python

También dijo (en la primera versión de su pregunta, antes de editar) que comienza con sus scripts Python #!/bin/sh read by the interpretor. Si lo dices literalmente, entonces definitivamente deberías dejar de hacerlo. Si hello.pycomienza con esa línea, la ejecución se ./hello.pyejecuta:

/bin/sh read by the interpretor hello.py

/bin/shintentará ejecutar un script llamado read(con by the interpretor hello.pysus argumentos), read(con suerte) no será encontrado, y su script Python nunca será visto por un intérprete de Python.

Si está cometiendo este error pero no tiene el problema que estoy describiendo, probablemente esté invocando sus scripts de Python especificando explícitamente el intérprete (por ejemplo, python hello.py), haciendo que se ignore la primera línea. Cuando distribuye sus scripts a otros, o los usa mucho tiempo después, puede que no esté claro que esto sea necesario para que funcionen. Es mejor arreglarlos ahora. O al menos elimine la primera línea por completo, de modo que cuando no se ejecuten con ./el mensaje de error tenga sentido.

Para los scripts de Python, si sabe dónde está (o va a estar) el intérprete de Python, puede escribir la #!línea de la misma manera:

#!/usr/bin/python

O, si se trata de un script de Python 3, debe especificar python3, ya pythonque casi siempre es Python 2 :

#!/usr/bin/python3

Sin embargo, el problema es que si bien /bin/shse supone que siempre existe, y /bin/bashcasi siempre existe en los sistemas que bashvienen con el sistema operativo, Python puede existir en una variedad de lugares.

Por lo tanto, muchos programadores de Python usan esto en su lugar:

#!/usr/bin/env python

(O #!/usr/bin/env python3para Python 3.)

Esto hace que el script se base en envestar en el "lugar correcto" en lugar de confiar en pythonestar en el lugar correcto. Eso es bueno porque:

  • envcasi siempre se encuentra en /usr/bin.
  • En la mayoría de los sistemas, lo que python debe ejecutar la secuencia de comandos es la que aparece por primera vez en el PATH. Comenzando hello.pycon #!/usr/bin/env pythonmake ./hello.pyrun /usr/bin/env python hello.py, que es (virtualmente) equivalente a correr python hello.py.

La razón por la que no puede usar #!pythones que:

  • Desea que el intérprete especificado sea dado por una ruta absoluta (es decir, comenzando con /)
  • El proceso de llamada se ejecutaría python en el directorio actual . Buscar la ruta cuando el comando no contiene una barra oblicua es un comportamiento de shell específico.

Ocasionalmente, un Python u otro script que no sea un script de shell tendrá una línea shebang que comienza #!/bin/sh ...donde ...hay algún otro código. Esto a veces es correcto, porque hay algunas formas de invocar el shell compatible con Bourne ( sh) con argumentos para que invoque un intérprete de Python. (Uno de los argumentos probablemente contendrá python). Sin embargo, para la mayoría de los propósitos, #!/usr/bin/env pythones más simple, más elegante y es más probable que funcione de la manera que desee.

Shebang Lines en otros idiomas

Muchos lenguajes de programación y secuencias de comandos, y algunos otros formatos de archivo, se usan #como comentario. Para cualquiera de ellos, un archivo en el lenguaje puede ser ejecutado por un programa que lo toma como argumento especificando el programa en la primera línea después #!.

En algunos lenguajes de programación, #normalmente no es un comentario, pero como caso especial se ignora la primera línea si comienza con #!. Esto facilita el uso de la #!sintaxis aunque de #otra manera no hace que una línea sea un comentario.

Líneas Shebang para archivos que no se ejecutan como scripts

Si bien es menos intuitivo, cualquier archivo cuyo formato de archivo puede acomodar una primera línea que comienza con #!seguido de la ruta completa de un ejecutable puede tener una línea shebang. Si hace esto, y el archivo está marcado como ejecutable, puede ejecutarlo como un programa ... haciendo que se abra como un documento.

Algunas aplicaciones usan este comportamiento intencionalmente. Por ejemplo, en VMware, los .vmxarchivos definen máquinas virtuales. Puede "ejecutar" una máquina virtual como si fuera un script porque estos archivos están marcados como ejecutables y tienen una línea shebang que hace que se abran en una utilidad VMware.

Shebang Lines para archivos que no se ejecutan como scripts pero que, de todos modos, actúan como scripts

rmelimina archivos No es un lenguaje de script. Sin embargo, #!/bin/rmse puede ejecutar un archivo que se inicia y está marcado como ejecutable, y cuando lo ejecuta, rmse invoca y lo elimina.

Esto a menudo se conceptualiza como "el archivo se elimina a sí mismo". Pero el archivo no se está ejecutando en absoluto. Esto se parece más a la situación descrita anteriormente para los .vmxarchivos.

Aún así, debido a que la #!línea facilita la ejecución de un comando simple (incluidos los argumentos de la línea de comandos), puede realizar algunas secuencias de comandos de esta manera. Como un ejemplo simple de un "script" más sofisticado que #!/bin/rm, considere:

#!/usr/bin/env tee -a

Esto toma la entrada del usuario de forma interactiva, la devuelve eco al usuario línea por línea y la agrega al final del archivo "script".

¿Útil? No muy. ¿Conceptualmente interesante? ¡Totalmente! Si. (Algo.)

Conceptos de programación / scripting conceptualmente similares (solo por diversión)


@Rinzwind Thx! (Por cierto, esta respuesta no se origina en otro lugar, si eso es lo que te estás preguntando).
Eliah Kagan

@Rinzwind No se preocupe, con 8 votos a favor después de 1 hora, es probable que aumente mucho más :-)
guntbert

1
Si siempre se ignora, ¿qué hace la -xbandera de Pythons ?
gerrit

44
@gerrit Buena pregunta. En cualquier idioma donde el intérprete / compilador informa mensajes con números de línea, los contenidos de los comentarios se ignoran, pero las líneas de comentarios aún se cuentan . Agregar un comentario o una línea en blanco antes de una línea de código todavía da como resultado que esa línea de código incremente su número de línea. -x"omitir [s] la primera línea ..." la segunda línea se numera en 1lugar de 2la tercera línea en 2lugar de 3, etc. Es por eso que no debes usar esa bandera. ;) -xes para crear scripts en sistemas operativos que no son de Unix que tienen una sintaxis similar a shebang que no comienza #(por lo tanto, no es un comentario de Python).
Eliah Kagan

44
En Perl, si el intérprete se inicia directamente ( perl script.plvs ./script.pl), entonces el intérprete se lee la línea tinglado para analizar banderas tales como -w. Sin embargo, no se recomienda confiar en esta característica.
Deja de dañar a Mónica el

7

Un shebang es la secuencia de caracteres que consiste en el signo de número de caracteres y el signo de exclamación (por ejemplo, "#!") Cuando aparece como los dos caracteres iniciales en la línea inicial de un guión.

En los sistemas operativos * nix, cuando se ejecuta un script que comienza con un shebang, el cargador de programas analiza el resto de la línea inicial del script como una directiva de intérprete; en su lugar, se ejecuta el programa de intérprete especificado, pasándole como argumento la ruta que se usó inicialmente al intentar ejecutar el script. Por ejemplo, si un script se nombra con la ruta "path / to / your-script", y comienza con la siguiente línea:

#!/bin/sh

luego se le indica al cargador del programa que ejecute el programa "/ bin / sh", por ejemplo, el shell Bourne o un shell compatible, pasando "path / to / your-script" como primer argumento.

En consecuencia, se llama un script con la ruta "path / to / python-script" y comienza con la siguiente línea:

#!/bin/python

entonces el programa cargado recibe instrucciones de ejecutar el programa "/ bin / python", por ejemplo, intérprete de Python, pasando "path / to / python-script" como primer argumento.

En resumen "#" comentará una línea mientras que la secuencia de caracteres "#!" que ocurre como los dos primeros caracteres en la línea inicial de un guión tiene un significado descrito como anteriormente.

Para obtener detalles, consulte ¿Por qué algunos scripts comienzan con #! ...?

Fuente: Algunas secciones de esta respuesta se derivan (con una ligera modificación) de Shebang (Unix) en Wikipedia en inglés (por colaboradores de Wikipedia ). Este artículo está licenciado bajo CC-BY-SA 3.0 , igual que el contenido del usuario aquí en AU, por lo que esta derivación está permitida con atribución.


4

#!se llama shebangcuando ocurre como los dos caracteres iniciales en la línea inicial de un script. Se usa en scripts para indicar un intérprete para la ejecución. El shebanges para el sistema operativo (kernel), no para el shell; así que no se interpretará como un comentario.

Cortesía: http://en.wikipedia.org/wiki/Shebang_%28Unix%29

En general, si un archivo es ejecutable, pero en realidad no es un programa ejecutable (binario), y dicha línea está presente, el programa especificado después de #! se inicia con el nombre del script y todos sus argumentos. Estos dos caracteres # y! tienen que ser los primeros dos bytes en el archivo!

Información detallada: http://wiki.bash-hackers.org/scripting/basics#the_shebang


0

No, solo lo usa la execllamada al sistema del kernel de Linux y el intérprete lo trata como un comentario.

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 integrado 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í menciona en las secciones de justificación, y en la forma "si el sistema admite scripts ejecutables, algo puede ocurrir".

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.