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?

Daniel YC Lin
fonte
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
serverfault.com/questions/328101/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

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.

Vielmetti
fonte
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
110
grep -a

Não pode ser mais simples do que isso.

James Selvakumar
fonte
3
este é o mesmo grep --textque o paxdiablo mencionou 2 anos antes
user829755
4
Sim, exceto que isso não funcionará no OSX a menos que você faça o seguinte:LC_ALL="C" grep -a
Chris Stratton
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
paxdiablo
fonte
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
temperamental
fonte
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.

AB
fonte
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

Steven Penny
fonte
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

Kevin Buchs
fonte
3

Como James Selvakumar já disse, grep -afaz o truque. -a ou --text força Grep a tratar o fluxo de entrada como texto. Consulte a página de manual http://unixhelp.ed.ac.uk/CGI/man-cgi?grep

experimentar

cat test.log | grep -a somestring
DerKnorr
fonte
2

você pode fazer

strings test.log | grep -i

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

Mrid
fonte
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).

MattCollW
fonte
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.

Muurder
fonte