perl - ¿Por qué la línea shebang siempre debe ser la primera línea?
unix solaris (4)
¡No es solo que tiene que ser la primera línea, los personajes #!
tienen que ser los primeros dos bytes en el archivo. Que esto puede ejecutar scripts es una característica de shell, no una de sistema operativo, y no es específica para ningún lenguaje de scripting en particular.
Cuando se le dice al sistema que ejecute el contenido de un archivo, ya sea con .../path/to/bin/program
, o por la ruta análoga a través de la RUTA, examina los primeros bytes del archivo para buscar los ''números mágicos'' que revelan qué tipo de archivo es (puedes echar un vistazo a ese proceso usando el comando file (1)). Si se trata de un binario compilado, se cargará y ejecutará de manera adecuada, y si esos primeros dos bytes son #!
Hará el ''shebang-hack''.
El ''shebang-hack'' es un caso especial que utilizan algunos shells (de hecho, esencialmente cada uno, pero es una convención más que un requisito), en el que el shell lee los bytes restantes hasta una nueva línea, los interpreta como un nombre de archivo , y luego ejecuta ese archivo dándole el resto del archivo actual como entrada. Además de algunos detalles sobre los que probablemente pueda leer en otro lugar .
Algunas (versiones de) proyectiles permitirán primeras líneas bastante largas, algunas solo permitirán las cortas; algunos permiten múltiples argumentos, algunos permiten solo uno.
Si el archivo no comienza con #!
, pero parece ser texto, algunos shells intentarán heurísticamente ejecutarlo de todos modos. Csh (si mal no recuerdo) considera que es un script csh, y hay algunos casos complicados y arcanos que tienen que ver con el comportamiento de algunos proyectiles si la primera línea está en blanco, cuya vida es demasiado corta para recordar.
Hay detalles interesantes y extensos (¡y precisos, en el sentido de que coinciden con mis recuerdos!) En el # de Sven Mascheck. página .
Tengo un script simple de Perl como el siguiente:
#!/usr/bin/perl
use strict;
use warnings;
print "hello ! world/n";
Puedo ejecutar este script de la siguiente manera:
>temp.pl
hello ! world
>
si agrego algunos comentarios como este:
#this script is just for test
#the shebang
#!/usr/bin/perl
use strict;
use warnings;
print "hello ! world/n";
y cuando intento ejecutar, me da salida de la siguiente manera:
> temp.pl
use: Command not found.
use: Command not found.
print: Command not found.
>
El punto aquí es que la línea shebang siempre debe estar en la parte superior sin importar nada. pero ¿alguien puede explicarme por qué?
Al menos en los sistemas que cumplen con POSIX, el shebang se usa para indicarle al cargador ejecutable qué hacer con los archivos de texto que tienen el bit ejecutable establecido.
El cargador sabe qué hacer con los archivos binarios, comienzan con un "número mágico", generalmente relacionado con ELF estos días.
Por otro lado, los archivos de texto que no tienen un shebang son ejecutados por el shell compatible con POSIX disponible en la máquina, por eso tiene estos mensajes de error de shell:
use: Command not found.
use: Command not found.
print: Command not found.
Cuando el ejecutable no debe ser interpretado por el shell compatible con POSIX, debe decirle al cargador qué intérprete usar. Otros sistemas operativos, como Windows, eligen la extensión del archivo para resolverlo, pero Unix no utiliza ni se preocupa por las extensiones en este caso específico. Lo que usa es el shebang en la primera línea que indica qué intérprete de comandos usar. El único inconveniente es que el lenguaje de scripting debe ignorar esta primera línea. Este es, con suerte, el caso ya que #
es un prefijo de línea de comentario con la mayoría de los lenguajes de scripting.
A pesar de la creencia popular, los scripts portátiles no deberían tener un shebang en absoluto. En particular, #!/bin/sh
no se recomienda para ellos.
Además de las explicaciones anteriores, que se tratan en detalle aquí y aquí y aquí, hay algunas cosas especiales sobre el #!
y Perl que no han sido mencionados aún.
Perl lee el #!
línea y hace dos cosas. Primero, si la ruta de acceso no se ve como perl, ¡volverá a ejecutar el programa usando eso! Por ejemplo...
#!/bin/sh
echo "Hello world!"
Se ejecutará correctamente si se ejecuta como perl /path/to/that/program
. No sé por qué razón histórica Perl hace esto, pero es útil cuando pruebas múltiples idiomas con Test :: Harness.
¡Lo segundo es que Perl encuentra interruptores en el #!
línea y los aplica como si estuvieran en la línea de comando. Esta es la razón por la cual #!/usr/bin/perl -w
funciona para activar las advertencias.
Vale la pena mencionar que a diferencia de las otras partes del procesamiento de shebang, todo esto se hace dentro de Perl, no en Unix, y por lo tanto es portátil para Windows.
Otra nota de Perl + shebang es esta locura que puedes encontrar en la parte superior de muchos programas de Perl.
#!/usr/bin/perl
eval ''exec /usr/bin/perl -w -S $0 ${1+"$@"}''
if 0; # not running under some shell
A veces, en sistemas muy, muy, muy antiguos, #!
no funciona y el shell ejecuta el programa Perl. El eval
fuerza al shell para que primero vuelva a ejecutar el archivo con Perl. Como las sentencias de shell finalizan en nueva línea, no se ve if 0
. Perl ve el if 0
, por lo que no ejecuta el eval. Tanto Perl como Shell tienen operadores de eval
sintácticamente equivalentes que hacen que el truco funcione.
El shebang debe ser la primera línea, ya que es interpretado por el kernel , que mira los dos bytes al comienzo de un archivo ejecutable. Si estos son #!
el resto de la línea se interpreta como el ejecutable que se ejecutará y con el archivo de script disponible para ese programa. (Los detalles varían ligeramente, pero esa es la imagen).
Como el kernel solo verá los dos primeros caracteres y no tiene ninguna noción de líneas adicionales, debe colocar el hash bang en la línea 1.
Ahora, ¿qué ocurre si el kernel no puede ejecutar un archivo que comience con #!whatever
? El shell, al tratar de bifurcar un ejecutable y ser informado por el kernel de que no puede ejecutar el programa, como último recurso intenta interpretar el contenido del archivo como un script de shell. Como el shell no es perl, obtienes un montón de errores, exactamente lo mismo que si intentaras ejecutar
sh < temp.pl