Esta é a solução de uma linha solicitada (para shells recentes que têm "substituição de processo"):
grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l
Se nenhuma "substituição de processo" <(…)
estiver disponível, use grep como um filtro:
hexdump -v -e '/1 "%02x "' infile.bin | grep -o "ef be ad de" | wc -l
Abaixo está a descrição detalhada de cada parte da solução.
Valores de bytes de números hexadecimais:
Seu primeiro problema é fácil de resolver:
Essas seqüências de escape \ Xnn funcionam apenas na casca do peixe.
Altere o superior X
para o inferior x
e use printf (para a maioria das conchas):
$ printf -- '\xef\xbe\xad\xde'
Ou use:
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
Para aqueles shells que optam por não implementar a representação '\ x'.
Obviamente, traduzir hex para octal funcionará em (quase) qualquer shell:
$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'
Onde "$ sh" é qualquer shell (razoável). Mas é bastante difícil mantê-lo corretamente citado.
Arquivos binários.
A solução mais robusta é transformar o arquivo e a sequência de bytes (ambos) em alguma codificação que não apresenta problemas com valores de caracteres ímpares como (nova linha) 0x0A
ou (byte nulo) 0x00
. Ambos são bastante difíceis de gerenciar corretamente com ferramentas projetadas e adaptadas para processar "arquivos de texto".
Uma transformação como base64 pode parecer válida, mas apresenta o problema de que cada byte de entrada pode ter até três representações de saída, dependendo do primeiro, segundo ou terceiro byte da posição mod 24 (bits).
$ echo "abc" | base64
YWJjCg==
$ echo "-abc" | base64
LWFiYwo=
$ echo "--abc" | base64
LS1hYmMK
$ echo "---abc" | base64 # Note that YWJj repeats.
LS0tYWJjCg==
Transformação hexadecimal.
É por isso que a transformação mais robusta deve ser aquela que começa no limite de cada byte, como a simples representação HEX.
Podemos obter um arquivo com a representação hexadecimal do arquivo com qualquer uma destas ferramentas:
$ od -vAn -tx1 infile.bin | tr -d '\n' > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' ' > infile.hex
A sequência de bytes a ser pesquisada já está em hexadecimal neste caso.
:
$ var="ef be ad de"
Mas também pode ser transformado. Um exemplo de uma viagem de ida e volta hex-bin-hex segue:
$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de
A cadeia de pesquisa pode ser definida a partir da representação binária. Qualquer uma das três opções apresentadas acima od, hexdump ou xxd são equivalentes. Apenas certifique-se de incluir os espaços para garantir que a correspondência esteja nos limites de bytes (nenhum deslocamento de mordidela é permitido):
$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de
Se o arquivo binário estiver assim:
$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074 This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70 est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120 ut ......from a
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131 bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000060: 3232 0a
Em seguida, uma simples pesquisa grep fornecerá a lista de sequências correspondentes:
$ grep -o "$a" infile.hex | wc -l
2
Uma linha?
Tudo pode ser realizado em uma linha:
$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l
Por exemplo, procurar 11221122
no mesmo arquivo precisará destas duas etapas:
$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4
Para "ver" as correspondências:
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232
$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
… 0a 313132323131323231313232313132323131323231313232313132323131313232 313132320a
Carregando
Há uma preocupação de que o grep armazene em buffer todo o arquivo e, se o arquivo for grande, criará uma carga pesada para o computador. Para isso, podemos usar uma solução sed sem buffer:
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -ue 's/\('"$a"'\)/\n\1\n/g' |
sed -n '/^'"$a"'$/p' |
wc -l
O primeiro sed é sem buffer ( -u
) e é usado apenas para injetar duas novas linhas no fluxo por sequência correspondente. O segundo sed
imprimirá apenas as linhas (curtas) correspondentes. O wc -l contará as linhas correspondentes.
Isso armazenará apenas algumas linhas curtas. A (s) string (s) correspondente (s) no segundo sed. Isso deve ser bastante baixo em recursos utilizados.
Ou, um pouco mais complexo de entender, mas a mesma ideia em um sed:
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
wc -l
grep -o