Maneira mais rápida de determinar se shebang está presente

Maneira mais rápida de determinar se shebang está presente

Se eu tiver um arquivo com

#!/usr/bin/env foobar

qual é a maneira mais rápida/melhor de determinar se este arquivo possui um hashbang? Ouvi dizer que você pode ler apenas os primeiros 2 bytes? Como?

Responder1

Com zsh:

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

O mesmo com ksh93ou bash:

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

embora bashdaria falsos positivos para arquivos que começam com NULs seguidos por#! e seriam lidostodosos bytes NUL principais leriam um arquivo de um tebibyte criado com truncate -s1T file2 bytes por vez, por exemplo.

Então, com bash, seria melhor usar:

IFS= LC_ALL=C read -rn2 -d '' shebang

Isso é lidoaté2 bytes de um registro delimitado por NUL.

Eles não bifurcam processos nem executam comandos extras, pois o read, [e echoos comandos são todos integrados.

POSIXly, você pode fazer:

if IFS= read -r line < file; then
  case $line in
    ("#!"*) echo has shebang
  esac
fi

É mais rigoroso porque também requer uma linha completa. Pelo menos no Linux, a nova linha não é necessária para um shebang válido.

Então você poderia fazer:

line=
IFS= read -r line < file
case $line in
  ("#!"*) echo has shebang
esac

É um pouco menos eficiente porque potencialmente leria mais bytes, com alguns shells um byte por vez. Com nosso arquivo esparso de 1TiB, isso levaria muito tempo na maioria dos shells (e potencialmente usaria muita memória).

Com shells diferentes de zsh, também pode fornecer falsos positivos para arquivos que começam com NULs seguidos de #!.

Com o yashshell, ele falharia se o shebang contivesse sequências de bytes que não formassem caracteres válidos no código do idioma atual (até falharia (pelo menos com 2.39 e anteriores) se o shebang contivesse caracteres não-ASCII no código do idioma C, mesmo que a localidade C seja aquela em que todos os caracteres são bytes únicos e todos os valores de bytes formam caracteres válidos - mesmo que não necessariamente definidos -)

Se você quiser encontrar todos os arquivos cujo conteúdo começa com #!, você pode fazer:

PERLIO=raw find . -type f -size +4c -exec perl -T -ne '
  BEGIN{$/=\2} print "$ARGV\n" if $_ eq "#!"; close ARGV' {} +

Estamos considerando apenas arquivos com pelo menos 5 bytes de tamanho ( #!/x\no valor mínimo realista).

  • com -exec perl... {} +, passamos tantos caminhos de arquivo perlquanto possível, então execute o mínimo de invocações possível
  • -Té contornaressa limitação deperl -ne também significa que não funcionará para arquivos cujo nome termina em caracteres de espaçamento ASCII ou |.
  • PERLIO=rawfaz com perlque as chamadas do sistema sejam usadas read()diretamente, sem qualquer camada de buffer de IO (também afeta a impressão de nomes de arquivos), portanto, fará leituras de tamanho 2.
  • $/ = \2quando o separador de registros é definido como uma referência a um número, ele faz com que os registros tenham comprimento fixo.
  • close ARGVpula o resto do arquivo atual depois de lermos o primeiro registro.

Responder2

Você pode definir seus próprios "padrões mágicos" /etc/magice usá-los filepara testar:

$ 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é hexadecimal de '#!' Em ordem inversa:

$ 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 você pode colocar:

0 string \#\! shebang is present

referência: man 5 magic, man 1 file, man 1posix file

Responder3

Isso deve resolver:

if [ "`head -c 2 infile`" = "#!" ]; then
    echo "Hashbang present"
else
    echo "no Hashbang present"
fi

Responder4

usar grepem uma solução de uma linha

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

informação relacionada