Quero contar quantas vezes uma determinada sequência de bytes acontece dentro de um arquivo que eu tenho. Por exemplo, quero descobrir quantas vezes o número \0xdeadbeef
ocorre dentro de um arquivo executável. No momento, estou fazendo isso usando o grep:
#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file
(Os bytes são escritos em ordem inversa porque minha CPU é little-endian)
No entanto, tenho dois problemas com minha abordagem:
- Essas
\Xnn
seqüências de escape funcionam apenas na casca do peixe. - grep está contando o número de linhas que contêm meu número mágico. Se o padrão ocorrer duas vezes na mesma linha, contará apenas uma vez.
Existe uma maneira de corrigir esses problemas? Como posso executar esse liner único no shell Bash e contar com precisão o número de vezes que o padrão ocorre dentro do arquivo?
bash
grep
escape-characters
hugomg
fonte
fonte
grep -o
11221122
, como deve ser retornado em uma entrada112211221122
? 1 ou 2?Respostas:
Esta é a solução de uma linha solicitada (para shells recentes que têm "substituição de processo"):
Se nenhuma "substituição de processo"
<(…)
estiver disponível, use grep como um filtro: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:
Altere o superior
X
para o inferiorx
e use printf (para a maioria das conchas):Ou use:
Para aqueles shells que optam por não implementar a representação '\ x'.
Obviamente, traduzir hex para octal funcionará em (quase) qualquer shell:
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).
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:
A sequência de bytes a ser pesquisada já está em hexadecimal neste caso.
:
Mas também pode ser transformado. Um exemplo de uma viagem de ida e volta hex-bin-hex segue:
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):
Se o arquivo binário estiver assim:
Em seguida, uma simples pesquisa grep fornecerá a lista de sequências correspondentes:
Uma linha?
Tudo pode ser realizado em uma linha:
Por exemplo, procurar
11221122
no mesmo arquivo precisará destas duas etapas:Para "ver" as correspondências:
… 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:
O primeiro sed é sem buffer (
-u
) e é usado apenas para injetar duas novas linhas no fluxo por sequência correspondente. O segundosed
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:
fonte
grep
que o carregamento será inteiro na memória (aqui o dobro do tamanho do arquivo original + 1 por causa da codificação hexadecimal); portanto, no final, ele acaba sendo mais sobrecarga do que apython
abordagem ouperl
aquele com-0777
. Você também precisa de umagrep
implementação que suporte linhas de comprimento arbitrário (aquelas que-o
geralmente suportam ). Boa resposta caso contrário.od -An -tx1 | tr -d '\n'
ouhexdump -v -e '/1 " %02x"'
com uma sequência de pesquisa que também contenha espaços, evite isso, mas não vejo essa correçãoxxd
.sed -u
(quando disponível) é para o buffer. Isso significa que ele lerá um byte de cada vez na entrada e enviará sua saída imediatamente sem buffer. Em qualquer caso, ele ainda precisará carregar toda a linha no espaço do padrão, portanto não ajudará aqui.Com GNU
grep
's-P
(perl-regexp) flagLC_ALL=C
é evitar problemas em códigos de idioma de vários bytes quegrep
, de outra forma, tentariam interpretar sequências de bytes como caracteres.-a
trata arquivos binários equivalentes a arquivos de texto (em vez do comportamento normal, ondegrep
apenas imprime se há pelo menos uma correspondência ou não)fonte
grep
que ele corresponda?-a
opção, caso contrário, o grep responderá comBinary file file.bin matches
qualquer arquivo que o grep detectar como binário.Que trata o (s) arquivo (s) de entrada como binário (sem conversão para alimentações de linha ou codificações, consulte perlrun ), circula o (s) arquivo (s) de entrada não imprimindo incrementando um contador para todas as correspondências do hexadecimal fornecido (ou qualquer outra forma, consulte perlre ) .
fonte
-0ooo
).$/
, pois , com uma troca ligeiramente diferente (uso de memória proporcional à distância máxima entre essas seqüências):perl -nE 'BEGIN { $/ = "\xef\xbe\xad\xde" } chomp; $c++ unless eof && length; END { say $c }'
Com o GNU
awk
, você pode fazer:Se algum dos bytes for um operador ERE, eles terão que ser escapados (com
\\
). Como o0x2e
que é.
teria que ser inserido como\\.
ou\\\x2e
. Fora isso, ele deve funcionar com valores de bytes arbitrários, incluindo 0 e 0xa.Observe que não é tão simples quanto apenas
NR-1
porque existem alguns casos especiais:RT==""
.Observe também que, na pior das hipóteses (se o arquivo não contiver o termo de pesquisa), o arquivo acabará sendo carregado inteiro na memória).
fonte
A tradução mais direta que vejo é:
Onde eu usei
$'\xef'
como a festa ANSI-citando (originalmente umksh93
recurso, agora apoiado porzsh
,bash
,mksh
, FreeBSDsh
) versão de peixes de\Xef
, e usadosgrep -o ... | wc -l
para contar os casos.grep -o
produz cada partida em uma linha separada. A-a
sinalização faz com que o grep se comporte nos arquivos binários da mesma forma que nos arquivos de texto.-F
é para cadeias fixas, para que você não precise escapar dos operadores de expressões regulares.Como no seu
fish
caso, você não pode usar essa abordagem se a sequência a procurar incluir os bytes 0 ou 0xa (nova linha em ASCII).fonte
printf '%b' $(printf '\\%o ' $((0xef)) $((0xbe)) $((0xad)) $((0xde))) > hugohex'
seria o método "shell puro" mais portátil. Claro:printf "efbeadde" | xxd -p -r > hugohex
parece ser o método mais prático.Você pode usar o
bytes.count
método Python para obter o número total de substrings não sobrepostos em uma bytestring.Esse liner carregará o arquivo inteiro na memória, portanto não é o mais eficiente, mas funciona e é mais legível que o Perl; D
fonte
239I$ 190I$ 173I$ 222I$ HXA ERfile$Y 0UC <:S^EQA$; %C$> QC=
(gd & r)mmap()
um arquivo em Python ; isso reduziria a confirmação da memória.fonte
Eu acho que você pode usar Perl, tente:
O comando Replace
s
fornece o número de substituições feitas, -0777 significa não tratar a nova linha como caractere especial,e
- execute o comando,say
para imprimir o que vem a seguir e, em seguida, imprima o novo caractere de linha,n
eu não tinha entendido completamente, mas não funciona sem / - from docs:fonte