O que faz o grep considerar um arquivo como binário?

185

Eu tenho alguns despejos de banco de dados de um sistema Windows na minha caixa. Eles são arquivos de texto. Estou usando o cygwin para cumprimentá-los. Estes parecem ser arquivos de texto simples; Abro-os com editores de texto, como o bloco de notas e o wordpad, e eles parecem legíveis. No entanto, quando eu executo grep, eles dirão binary file foo.txt matches.

Percebi que os arquivos contêm alguns NULcaracteres ASCII , que acredito serem artefatos do despejo de banco de dados.

Então, o que faz o grep considerar esses arquivos como binários? O NULpersonagem? Existe uma bandeira no sistema de arquivos? O que preciso alterar para que o grep me mostre as correspondências da linha?

user394
fonte
2
--null-datapode ser útil se NULfor o delimitador.
Steve-o

Respostas:

125

Se houver um NULcaractere em qualquer lugar do arquivo, o grep o considerará um arquivo binário.

Pode haver uma solução alternativa como essa cat file | tr -d '\000' | yourgreppara eliminar todos os nulos primeiro e depois pesquisar no arquivo.

bbaja42
fonte
149
... ou use -a/ --text, pelo menos com o GNU grep.
Derobert
1
@derobert: na verdade, em alguns sistemas (mais antigos), o grep vê linhas, mas sua saída truncará cada linha correspondente na primeira NUL(provavelmente porque chama o printf de C e fornece a linha correspondente?). Nesse sistema, a grep cmd .sh_historyretornará quantas linhas vazias houver linhas correspondentes a 'cmd', pois cada linha de sh_history possui um formato específico com a NULno início de cada linha. (mas o seu comentário "pelo menos no GNU grep" provavelmente vem verdade eu não tiver um na mão agora para testar, mas espero que eles lidar com isso muito bem.)
Olivier Dulac
4
A presença de um caractere NUL é o único critério? Eu duvido. Provavelmente é mais inteligente que isso. Qualquer coisa fora do intervalo Ascii 32-126 seria o meu palpite, mas teríamos que olhar o código fonte para ter certeza.
Michael Martinez
2
Minhas informações eram da página de manual da instância grep específica. Seu comentário sobre a implementação é válido, a fonte supera os documentos.
precisa saber é o seguinte
2
Eu tinha um arquivo que grepno cygwin considerava binário porque tinha um traço longo (0x96) em vez de um hífen ASCII normal / menos (0x2d). Acho que essa resposta resolveu o problema do OP, mas parece que está incompleto.
cp.engr
121

grep -a trabalhou para mim:

$ grep --help
[...]
 -a, --text                equivalent to --binary-files=text
Plouff
fonte
4
Esta é a melhor e mais barata resposta IMO.
pydsigner
Mas não compatível com POSIX
Matteo
21

Você pode usar o stringsutilitário para extrair o conteúdo de texto de qualquer arquivo e depois canalizá-lo através de grep, como este: strings file | grep pattern.

holgero
fonte
2
Ideal para grepping arquivos de log que pode ser parcialmente corrompido
Hannes R.
Sim, às vezes o log misto binário também acontece. Isso é bom.
Sdkks 03/09/19
13

GNU grep 2.24 RTFS

Conclusão: 2 e 2 casos apenas:

  • NUL, por exemplo printf 'a\0' | grep 'a'

  • erro de codificação de acordo com o C99 mbrlen(), por exemplo:

    export LC_CTYPE='en_US.UTF-8'
    printf 'a\x80' | grep 'a'
    

    porque \x80não pode ser o primeiro byte de um ponto Unicode UTF-8 : UTF-8 - Descrição | en.wikipedia.org

Além disso, como mencionado por Stéphane Chazelas O que faz o grep considerar um arquivo como binário? | Unix e Linux Stack Exchange , essas verificações são feitas apenas até a primeira leitura do buffer com o comprimento TODO.

Somente até a primeira leitura do buffer

Portanto, se ocorrer um erro de codificação ou NUL no meio de um arquivo muito grande, ele poderá ser recebido de qualquer maneira.

Eu imagino que isso seja por razões de desempenho.

Por exemplo: isso imprime a linha:

printf '%10000000s\n\x80a' | grep 'a'

mas isso não:

printf '%10s\n\x80a' | grep 'a'

O tamanho real do buffer depende de como o arquivo é lido. Por exemplo, compare:

export LC_CTYPE='en_US.UTF-8'
(printf '\n\x80a') | grep 'a'
(printf '\n'; sleep 1; printf '\x80a') | grep 'a'

Com o sleep, a primeira linha é passada para o grep, mesmo que tenha apenas 1 byte, porque o processo entra no modo de espera ea segunda leitura não verifica se o arquivo é binário.

RTFS

git clone git://git.savannah.gnu.org/grep.git 
cd grep
git checkout v2.24

Localize onde a mensagem de erro stderr está codificada:

git grep 'Binary file'

Nos leva a /src/grep.c:

if (!out_quiet && (encoding_error_output
                    || (0 <= nlines_first_null && nlines_first_null < nlines)))
    {
    printf (_("Binary file %s matches\n"), filename);

Se essas variáveis ​​foram bem nomeadas, chegamos basicamente à conclusão.

encoding_error_output

O grepping rápido para encoding_error_outputmostra que o único caminho de código que pode modificá-lo passa por buf_has_encoding_errors:

clen = mbrlen (p, buf + size - p, &mbs);
if ((size_t) -2 <= clen)
  return true;

então apenas man mbrlen.

nlines_first_null e nlines

Inicializado como:

intmax_t nlines_first_null = -1;
nlines = 0;

portanto, quando um nulo é encontrado, 0 <= nlines_first_nulltorna-se verdadeiro.

TODO quando pode nlines_first_null < nlinesser falso? Eu fiquei com preguiça

POSIX

Não define opções binárias grep - pesquisa um padrão em um arquivo | pubs.opengroup.org e GNU grep não o documentam, então o RTFS é a única maneira.

Ciro Santilli adicionou uma nova foto
fonte
1
Explicação impressionante!
precisa saber é o seguinte
2
Observe que a verificação de UTF-8 válido ocorre apenas nos códigos de idioma UTF-8. Observe também que a verificação é feita apenas na primeira leitura do buffer do arquivo, que para um arquivo normal parece ter 32768 bytes no meu sistema, mas para um tubo ou soquete pode ser tão pequeno quanto um byte. Compare (printf '\n\0y') | grep ycom, (printf '\n'; sleep 1; printf '\0y') | grep ypor exemplo.
Stéphane Chazelas 13/04
@ StéphaneChazelas "Note que a verificação de UTF-8 válido só acontece em localidades UTF-8": você quer dizer isso export LC_CTYPE='en_US.UTF-8'como no meu exemplo, ou algo mais? Buf read: amazing example, added to answer. Você, obviamente, ler a fonte mais do que eu, me lembra daqueles koans hackers "O estudante estava iluminado" :-)
Ciro Santilli新疆改造中心法轮功六四事件
1
Eu não olhei em grandes detalhes, quer, mas fez muito recentemente
Stéphane Chazelas
1
@CiroSantilli 法轮功 文件 六四 事件 Em qual versão do GNU grep você testou?
Jrw32982
6

Um dos meus arquivos de texto foi subitamente visto como binário pelo grep:

$ file foo.txt
foo.txt: ISO-8859 text

A solução foi convertê-lo usando iconv:

iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt
zzapper
fonte
1
Isso também me aconteceu. Em particular, a causa foi um espaço sem quebra codificado em ISO-8859-1, que tive que substituir por um espaço regular para fazer com que o grep procurasse no arquivo.
Gallaecio
4
O grep 2.21 trata os arquivos de texto ISO-8859 como se fossem binários; adicione export LC_ALL = C antes do comando grep.
Netawater 17/08/2015
Obrigado @netawater! É o caso, por exemplo, se você tiver algo como Müller em um arquivo de texto. Isso é 0xFChexadecimal, portanto, fora do intervalo, o grep esperaria pelo utf8 (até 0x7F). Verifique com printf 'a \ x7F' | grep 'a' como Ciro descreve acima.
Anne van Rossum
5

O arquivo /etc/magicou /usr/share/misc/magicpossui uma lista de sequências que o comando fileusa para determinar o tipo de arquivo.

Observe que o binário pode ser apenas uma solução alternativa. Às vezes, arquivos com codificação estranha também são considerados binários.

grepno Linux tem algumas opções para lidar com arquivos binários como --binary-filesou-U / --binary

klapaucius
fonte
Mais precisamente, erro de codificação de acordo com C99 mbrlen(). Exemplo e interpretação da fonte em: unix.stackexchange.com/a/276028/32558
Ciro Santilli (
2

Um dos meus alunos teve esse problema. Há um erro grepno Cygwin. Se o arquivo tiver caracteres não-Ascii, grepe egrepveja-o como binário.

Joan Pontius
fonte
Isso soa como um recurso, não um bug. Especialmente tendo em conta que há uma opção de linha de comando para controlá-lo (-a / --text)
Será Sheppard
2

Na verdade, respondendo à pergunta "O que faz o grep considerar um arquivo como binário?", Você pode usar iconv:

$ iconv < myfile.java
iconv: (stdin):267:70: cannot convert

No meu caso, havia caracteres em espanhol que apareciam corretamente nos editores de texto, mas o grep os considerava binários; iconvsaída me apontou para os números de linha e coluna desses caracteres

No caso de NULcaracteres, iconvconsiderá-los-á normais e não imprimirá esse tipo de saída, portanto esse método não é adequado

golimar
fonte
1

Eu tive o mesmo problema. Eu costumava vi -b [filename]ver os caracteres adicionados. Eu encontrei os caracteres de controle ^@e ^M. Em seguida, no tipo vi, :1,$s/^@//gpara remover os ^@caracteres. Repita este comando para ^M.

Aviso: Para obter os caracteres de controle "azuis", pressione Ctrl+ ve Ctrl+ Mou Ctrl+ @. Em seguida, salve e saia do vi.

Não tenho certeza
fonte