La forma más rápida de determinar si hay shebang presente

La forma más rápida de determinar si hay shebang presente

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 ksh93o bash:

if IFS= LC_ALL=C read -rN2 shebang < file && [ "$shebang" = '#!' ]; then
  echo has shebang
fi

aunque bashdarí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 file2 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 echotodos 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 yashshell, 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\nel mínimo realista).

  • con -exec perl... {} +, pasamos tantas rutas de archivos perlcomo sea posible para ejecutar la menor cantidad de invocaciones posible
  • -Tes trabajar alrededoresa limitación deperl -ny también significa que no funcionará con archivos cuyo nombre termine en caracteres de espaciado ASCII o |.
  • PERLIO=rawhace perlque se utilicen read()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.
  • $/ = \2cuando el separador de registros se establece como referencia a un número, hace que los registros sean de longitud fija.
  • close ARGVomite el resto del archivo actual después de haber leído el primer registro.

Respuesta2

Puede definir sus propios "patrones mágicos" /etc/magicy utilizarlos filepara 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

0x2123es 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 grepen una solución de una sola línea

if head -1 file | grep "^#\!" > /dev/null;then echo "true"; fi

información relacionada