Como fazer o grep em um arquivo de texto que contém alguns dados binários?


122

grep retorna

Arquivo binário test.log corresponde

Por exemplo

echo    "line1 re \x00\r\nline2\r\nline3 re\r\n" > test.log  # in zsh
echo -e "line1 re \x00\r\nline2\r\nline3 re\r\n" > test.log  # in bash
grep re test.log

Desejo que o resultado mostre a linha 1 e a linha 3 (total de duas linhas).

É possível trconverter os dados não imprimíveis em dados legíveis, para deixar o grep funcionar novamente?


Observe que existe um programa que filtra os caracteres binários de um arquivo binário e mantém apenas os caracteres de texto (legíveis). Aqui: soft.tahionic.com/download-words_extractor/index.html
InTheNameOfScience

Com licença, mas ... você não está faltando -eno echocomando?
Sopalajo de Arrierez

Se você usar 'zsh', está tudo bem sem -e. Se você usar 'bash', deverá adicionar '-e'.
Daniel YC Lin

Respostas:


67

Você pode executar o arquivo de dados cat -v, por exemplo

$ cat -v tmp/test.log | grep re
line1 re ^@^M
line3 re^M

que poderia ser posteriormente pós-processado para remover o lixo; isso é mais análogo à sua consulta sobre o uso trpara a tarefa.


5
Resolveu meu problema. Obrigado! Aqui está o que man catdiz sobre -v:-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB
tommy.carstensen

Observe que isso também funciona em um pipeline. Exset | cat -v | grep variable
funroll de

1
Por que usar isso se grep --text funcionar? Isso parece muito mais complexo.
Michael Haefele

grep --textnem sempre funciona; ele respeita CTRL + D como um terminador de arquivo. Portanto, se você tiver isso em seu arquivo binário, o grep sairá mais cedo.
Tommy


91

Uma maneira é simplesmente tratar os arquivos binários como texto de qualquer maneira, com grep --textmas isso pode muito bem resultar no envio de informações binárias ao seu terminal. Isso não é realmente uma boa ideia se você estiver executando um terminal que interpreta o fluxo de saída (como VT / DEC ou muitos outros).

Como alternativa, você pode enviar seu arquivo trcom o seguinte comando:

tr '[\000-\011\013-\037\177-\377]' '.' <test.log | grep whatever

Isso mudará qualquer coisa menor que um caractere de espaço (exceto nova linha) e qualquer coisa maior que 126 em um .caractere, deixando apenas os imprimíveis.


Se quiser que cada caractere "ilegal" seja substituído por um diferente, você pode usar algo como o seguinte programa C, um filtro de entrada padrão clássico:

#include<stdio.h>
int main (void) {
    int ch;
    while ((ch = getchar()) != EOF) {
        if ((ch == '\n') || ((ch >= ' ') && (ch <= '~'))) {
            putchar (ch);
        } else {
            printf ("{{%02x}}", ch);
        }
    }
    return 0;
}

Isso lhe dará {{NN}}, onde NNestá o código hexadecimal para o personagem. Você pode simplesmente ajustar o printfpara qualquer estilo de saída que desejar.

Você pode ver esse programa em ação aqui, onde:

pax$ printf 'Hello,\tBob\nGoodbye, Bob\n' | ./filterProg
Hello,{{09}}Bob
Goodbye, Bob

Este método mapeia todos os caracteres binários no mesmo '.' símbolo. Existe outro método que os mapeia para símbolos legíveis?
Daniel YC Lin

Claro, você pode executá-lo por meio de um programa de filtro diferente, um dos quais forneci em uma atualização.
paxdiablo

1
Eu acho tr '[:cntrl:] '.'melhor E deve estar \000-\010\013\014\016-\037\177-\377'em sua sintaxe tr.
Daniel YC Lin,

2
Após o teste, tr '[\000-\010\013\014\016-\037\177-\377]' '_'viável, o cntrl não é adequado para o meu caso.
Daniel YC Lin,

2
Você pode salvar a catetapa canalizando grep --textem trvez de vice-versa. Isso também permite que você execute grep em vários arquivos e mantenha a referência do nome do arquivo na saída.
aaaantoine

33

Você pode usar "strings" para extrair strings de um arquivo binário, por exemplo

strings binary.file | grep foo

Funcionou bem para mim, pois a fonte era um log de depuração com UID em cada linha. Obrigado.
mbrownnyc

funcionou bem para mim também. Obrigado pela sua resposta. Salvei meu dia :)
Shekhar

2
Agradeço a resposta de @paxdiablo, mas por uma resposta rápida e como continuar com o trabalho você não pode culpar isso.
Wil,

Tentei usar a solução paxdiablo, porém não me deu nenhum dos resultados que esperava. @moodywoody sua solução é rápida, simples e produz exatamente o que eu precisava!
justinhartman

20

Você pode forçar o grep a olhar os arquivos binários com:

grep --binary-files=text

Você também pode querer adicionar -o( --only-matching) para não obter toneladas de jargões binários que irão danificar seu terminal.


pode gerar lixo binário, que pode ter efeitos colaterais desagradáveis ​​se a saída for um terminal e se o driver do terminal interpretar parte dele como comandos.
Daniel YC Lin

Se você usar --only-matchinge sua regex não corresponder a dados binários arbitrários, não terá problemas.
AB

se a expressão regular for 'first. * end' e os dados binários contiverem no padrão '. *', ele não pode corrigir o processo para meu pós-processamento. Enfim, obrigado.
Daniel YC Lin

16

A partir do Grep 2.21, os arquivos binários são tratados de forma diferente :

Ao pesquisar dados binários, grep agora pode tratar bytes não textuais como terminadores de linha. Isso pode aumentar o desempenho significativamente.

Então, o que acontece agora é que, com dados binários, todos os bytes não textuais (incluindo novas linhas) são tratados como terminadores de linha. Se quiser mudar esse comportamento, você pode:

  • usar --text. Isso irá garantir que apenas novas linhas sejam terminadores de linha

  • usar --null-data. Isso irá garantir que apenas bytes nulos sejam terminadores de linha


5

grep -a forçará o grep a pesquisar e gerar a saída de um arquivo que o grep pensa ser binário. grep -a re test.log



2

você pode fazer

strings test.log | grep -i

isso converterá a saída em uma string legível para grep.


0

Você também pode experimentar a ferramenta Word Extractor . O Word Extractor pode ser usado com qualquer arquivo em seu computador para separar as strings que contêm texto / palavras humanas do código binário (aplicativos exe, DLLs).


No meu caso, não exijo extrator de palavras, exijo manter o número da linha.
Daniel YC Lin

0

Aqui está o que usei em um sistema que não tinha o comando "strings" instalado

cat yourfilename | tr -cd "[:print:]"

Isso imprime o texto e remove caracteres não imprimíveis de uma só vez, ao contrário de "cat -v filename" que requer algum pós-processamento para remover coisas indesejadas. Observe que alguns dos dados binários podem ser impressos, portanto, você ainda obterá alguma confusão entre as coisas boas. Eu acho que strings remove esse jargão também se você pode usar isso.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.