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 Xpara o inferior xe 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) 0x0Aou (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 11221122no 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 sedimprimirá 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