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 ksh93
ou bash
:
if IFS= LC_ALL=C read -rN2 shebang < file && [ "$shebang" = '#!' ]; then
echo has shebang
fi
embora bash
daria 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 file
2 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 echo
os 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 yash
shell, 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\n
o valor mínimo realista).
- com
-exec perl... {} +
, passamos tantos caminhos de arquivoperl
quanto possível, então execute o mínimo de invocações possível -T
é contornaressa limitação deperl -n
e também significa que não funcionará para arquivos cujo nome termina em caracteres de espaçamento ASCII ou|
.PERLIO=raw
faz comperl
que as chamadas do sistema sejam usadasread()
diretamente, sem qualquer camada de buffer de IO (também afeta a impressão de nomes de arquivos), portanto, fará leituras de tamanho 2.$/ = \2
quando o separador de registros é definido como uma referência a um número, ele faz com que os registros tenham comprimento fixo.close ARGV
pula o resto do arquivo atual depois de lermos o primeiro registro.
Responder2
Você pode definir seus próprios "padrões mágicos" /etc/magic
e usá-los file
para 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 grep
em uma solução de uma linha
if head -1 file | grep "^#\!" > /dev/null;then echo "true"; fi