Si tengo un archivo con
#!/usr/bin/env foobar
¿Cuál es la mejor y más rápida manera de determinar si este archivo tiene un hashbang? ¿Escuché que puedes leer solo los primeros 2 bytes? ¿Cómo?
Respuesta1
Con zsh
:
if LC_ALL=C read -u0 -k2 shebang < file && [ "$shebang" = '#!' ]; then
echo has shebang
fi
Lo mismo con ksh93
o bash
:
if IFS= LC_ALL=C read -rN2 shebang < file && [ "$shebang" = '#!' ]; then
echo has shebang
fi
aunque bash
daría falsos positivos para archivos que comiencen con NUL seguidos de #!
y leeríantodolos bytes NUL iniciales leerían un archivo de un tebibyte creado con truncate -s1T file
2 bytes a la vez, por ejemplo.
Entonces con bash
, sería mejor usar:
IFS= LC_ALL=C read -rn2 -d '' shebang
eso se leehasta2 bytes de un registro delimitado por NUL.
Estos no bifurcan procesos ni ejecutan comandos adicionales como read
, [
y echo
todos los comandos están integrados.
POSIXly, puedes hacer:
if IFS= read -r line < file; then
case $line in
("#!"*) echo has shebang
esac
fi
Es más estricto porque también requiere una línea completa. Sin embargo, al menos en Linux, la nueva línea no es necesaria para un asunto válido.
Entonces podrías hacer:
line=
IFS= read -r line < file
case $line in
("#!"*) echo has shebang
esac
Es un poco menos eficiente porque potencialmente leería más bytes, con algunos shells un byte a la vez. Con nuestro archivo disperso de 1TiB, eso llevaría mucho tiempo en la mayoría de los shells (y potencialmente consumiría mucha memoria).
Con shells distintos a zsh
, también podría dar falsos positivos para archivos que comienzan con NUL seguido de #!
.
Con el yash
shell, fallaría si el shebang contiene secuencias de bytes que no forman caracteres válidos en la configuración regional actual (incluso fallaría (al menos con 2.39 y versiones anteriores) si el shebang contuviera caracteres no ASCII en la configuración regional C, aunque la configuración regional C está destinada a ser aquella en la que todos los caracteres son bytes únicos y todos los valores de bytes forman caracteres válidos, incluso si no están necesariamente definidos)
Si desea encontrar todos los archivos cuyo contenido comienza con #!
, puede hacer:
PERLIO=raw find . -type f -size +4c -exec perl -T -ne '
BEGIN{$/=\2} print "$ARGV\n" if $_ eq "#!"; close ARGV' {} +
Solo estamos considerando archivos que tengan al menos 5 bytes de tamaño ( #!/x\n
el mínimo realista).
- con
-exec perl... {} +
, pasamos tantas rutas de archivosperl
como sea posible para ejecutar la menor cantidad de invocaciones posible -T
es trabajar alrededoresa limitación deperl -n
y también significa que no funcionará con archivos cuyo nombre termine en caracteres de espaciado ASCII o|
.PERLIO=raw
haceperl
que se utilicenread()
llamadas al sistema directamente sin ninguna capa de almacenamiento en búfer de E/S (también afecta la impresión de nombres de archivos), por lo que realizará lecturas de tamaño 2.$/ = \2
cuando el separador de registros se establece como referencia a un número, hace que los registros sean de longitud fija.close ARGV
omite el resto del archivo actual después de haber leído el primer registro.
Respuesta2
Puede definir sus propios "patrones mágicos" /etc/magic
y utilizarlos file
para probar:
$ sudo vi /etc/magic
$ cat /etc/magic
# Magic local data for file(1) command.
# Insert here your local magic data. Format is described in magic(5).
0 byte 0x2123 shebang is present
$ cat /tmp/hole2.sh #To prove [1] order of hex [2] 2nd line ignored
!#/bin/bash
#!/bin/bash
$ cat /tmp/hole.sh
#!/bin/bash
$ file /tmp/hole2.sh
/tmp/hole2.sh: ASCII text
$ file /tmp/hole.sh
/tmp/hole.sh: shebang is present
$ file -b /tmp/hole.sh #omit filename
shebang is present
0x2123
es hexadecimal de '#!' en orden inverso:
$ ascii '#' | head -n1
ASCII 2/3 is decimal 035, hex 23, octal 043, bits 00100011: prints as `#'
$ ascii '!' | head -n1
ASCII 2/1 is decimal 033, hex 21, octal 041, bits 00100001: prints as `!'
Opcionalmente puedes poner:
0 string \#\! shebang is present
referencia: man 5 magic
, man 1 file
, man 1posix file
Respuesta3
Deberias hacer eso:
if [ "`head -c 2 infile`" = "#!" ]; then
echo "Hashbang present"
else
echo "no Hashbang present"
fi
Respuesta4
utilizar grep
en una solución de una sola línea
if head -1 file | grep "^#\!" > /dev/null;then echo "true"; fi