Tenho alguns despejos de banco de dados de um sistema Windows em minha caixa. Eles são arquivos de texto. Estou usando o cygwin para fazer o grep por meio deles. Eles parecem ser arquivos de texto simples; eu os abro com editores de texto como notepad e wordpad e eles parecem legíveis. No entanto, quando eu executar o grep neles, ele mostrará binary file foo.txt matches
.
Percebi que os arquivos contêm alguns caracteres ascii NUL
, que acredito serem artefatos do despejo do banco de dados.
Então, o que faz o grep considerar esses arquivos binários? O caractere NUL
? Existe um sinalizador no sistema de arquivos? O que preciso mudar para colocar o grep mostrar as correspondências de linha?
Comentários
Resposta
Se houver um NUL
caractere em qualquer lugar no arquivo, grep irá considerá-lo como um arquivo binário.
Pode haver uma solução alternativa como esta cat file | tr -d "\000" | yourgrep
para eliminar todos nulos primeiro, e em seguida, para pesquisar no arquivo.
Comentários
- … ou use
-a
/--text
, pelo menos com GNU grep. - @derobert: na verdade, em alguns sistemas (mais antigos), grep vê as linhas, mas sua saída truncará cada linha correspondente no primeiro
NUL
(provavelmente porque chama C ‘ s printf e fornece a linha correspondente?). Nesse sistema, umgrep cmd .sh_history
retornará tantas linhas vazias quantas forem as linhas correspondentes a ‘ cmd ‘, pois cada linha de sh_history tem um formato específico com umNUL
no início de cada linha. (mas seu comentário ” pelo menos no GNU grep ” provavelmente se concretizou. Eu não ‘ não tenho um disponível agora para testar, mas espero que eles lidem bem com isso) - A presença de um caractere NUL é o único critério? Eu duvido. É ‘ provavelmente mais inteligente do 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.
- Minhas informações eram na página de manual da instância grep específica. Seu comentário sobre a implementação é válido, a fonte supera os documentos.
- Eu tinha um arquivo que
grep
no cygwin era considerado binário porque tinha um traço longo (0x96) em vez de um hífen / menos ASCII regular (0x2d). Acho que esta resposta resolveu o problema do OP ‘ s, mas parece que está incompleto.
Resposta
grep -a
funcionou para mim:
$ grep --help [...] -a, --text equivalent to --binary-files=text
Comentários
- Esta é a melhor e menos cara resposta IMO.
- Mas não compatível com POSIX
- Você se importaria de explicar por que não é? Seria bom deixar isso claro, para todos nós que encontramos essa resposta como uma opção. Obrigado :).
- Ei, ‘ vim aqui uma SEGUNDA vez para reaprender este LOL. Um sotaque francês (diacrítico) no texto estava causando grep para barf
Resposta
Você pode usar o strings
utilitário para extrair o conteúdo de texto de qualquer arquivo e, em seguida, canalizá-lo por meio de grep
, assim: strings file | grep pattern
.
Comentários
- Ideal para grepping arquivos de log que podem estar parcialmente corrompidos
- sim, às vezes, log binário misto também acontece. Isso é bom.
Resposta
GNU grep 2.24 RTFS
Conclusão: 2 e 2 casos apenas:
-
NUL
, por exemploprintf "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
\x80
não pode ser o primeiro byte de um ponto Unicode UTF-8: UTF-8 – Descrição | en.wikipedia.org
Além disso, conforme mencionado por Stéphane Chazelas O que faz o grep considerar um arquivo para ser binário? | Unix & Linux Stack Exchange , essas verificações são feitas apenas até o primeiro buffer lido de comprimento TODO.
Somente até o primeiro buffer ler
Portanto, se um NUL ou erro de codificação acontecer no meio de um arquivo muito grande, pode ser grepped de qualquer maneira.
Imagino que seja por motivos 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 grep, mesmo que seja apenas 1 byte longo porque o processo entra em suspensão e a 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
Descubra 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 fossem bem nomeadas, basicamente chegamos à conclusão.
encoding_error_output
Quick grepping para encoding_error_output
mostra 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;
e apenas man mbrlen
.
nlines_first_null e nlines
Inicializado como:
intmax_t nlines_first_null = -1; nlines = 0;
então, quando um nulo é encontrado, 0 <= nlines_first_null
torna-se verdadeiro.
TODO quando pode nlines_first_null < nlines
nunca será falso? Eu fiquei com preguiça.
POSIX
Não define opções binárias grep – pesquisa um arquivo para um padrão | pubs.opengroup.org , e GNU grep não documenta, então RTFS é a única maneira.
Comentários
- Explicação impressionante !
- Observe que a verificação de UTF-8 válido ocorre apenas em locales UTF-8. Observe também que a verificação é feita apenas no primeiro buffer lido do arquivo, que para um arquivo normal parece ter 32768 bytes no meu sistema, mas para um pipe ou socket pode ser tão pequeno quanto um byte. Compare
(printf '\n\0y') | grep y
com(printf '\n'; sleep 1; printf '\0y') | grep y
por exemplo. - @St é phaneChazelas ” Observe que a verificação de UTF-8 válido ocorre apenas em localidades UTF-8 “: você quer dizer sobre o
export LC_CTYPE='en_US.UTF-8'
como no meu exemplo, ou algo mais? Buf read: exemplo incrível, adicionado à resposta. Obviamente, você leu a fonte mais do que eu, lembra-me daqueles hacker koans ” O aluno foi esclarecido ” 🙂 - Eu não ‘ também analisei os detalhes, mas fez recentemente
- @CiroSantilli 巴拿馬 文件 六四 事件 法轮功 com qual versão do GNU grep você testou?
Resposta
Um dos meus arquivos de texto de repente foi visto como binário por 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
Comentários
- Isso aconteceu comigo também. Em particular, a causa foi um espaço sem quebra com codificação ISO-8859-1, que tive que substituir por um espaço regular para fazer o grep pesquisar no arquivo.
- grep 2.21 trata ISO -8859 arquivos de texto como se fossem binários, adicione export LC_ALL = C antes do comando grep.
- @netawater Obrigado! Este é, por exemplo o caso se você tiver algo como M ü ller em um arquivo de texto. Aquele ‘ s
0xFC
hexadecimal, portanto, fora do intervalo, grep esperaria para utf8 (até0x7F
). Verifique com printf ‘ a \ x7F ‘ | grep ‘ a ‘ como Ciro descreveu acima.
Resposta
O arquivo /etc/magic
ou /usr/share/misc/magic
tem uma lista de sequências que o comando file
usa 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.
grep
no Linux tem algumas opções para lidar com arquivos binários como --binary-files
ou -U / --binary
Comentários
- Mais precisamente, erro de codificação de acordo com C99 ‘ s
mbrlen()
. Exemplo e interpretação da fonte em: unix.stackexchange.com/a/276028/32558
Resposta
Um dos meus alunos teve este problema. Há um bug em grep
em Cygwin
. Se o arquivo tiver caracteres não ASCII, grep
e egrep
veja-o como binário.
Comentários
- Isso parece um recurso, não um bug.Especialmente porque há uma opção de linha de comando para controlá-lo (-a / –text)
Resposta
Respondendo à pergunta “O que faz o grep considerar um arquivo binário?”, Você pode usar iconv
:
$ iconv < myfile.java iconv: (stdin):267:70: cannot convert
No meu caso, havia caracteres espanhóis que apareciam corretamente nos editores de texto, mas o grep os considerava binários; iconv
a saída apontou-me para os números de linha e coluna desses caracteres
No caso de NUL
caracteres, iconv
irá considerá-los normais e não imprimirá esse tipo de saída, então este método não é adequado
Resposta
Eu tive o mesmo problema. Usei vi -b [filename]
para ver os caracteres adicionados. Encontrei os caracteres de controle ^@
e ^M
. Então, no vi, digite :1,$s/^@//g
para remover os ^@
caracteres. Repita este comando para ^M
.
Aviso: para obter os caracteres de controle “azuis”, pressione Ctrl + v e, em seguida, Ctrl + M ou Ctrl + @ . Em seguida, salve e saia do vi.
Resposta
Eu também tive este problema, mas no meu caso foi causado quando uma linha correspondente é muito longo.
file myfile.txt myfile.txt: UTF-8 Unicode text, with very long lines
grep
seria executado em todo o arquivo bem com muitos padrões, mas quando um padrão correspondeu a um ” linha muito longa ” parou com Binary file myfile.txt matches
.
Adicionar -a
também resolve esse problema, mas pré-analisar o arquivo para NULL ou outros caracteres inválidos não teria efeito (não há nenhum, caso contrário, o grep não seria concluído para outros padrões). Neste caso, a linha incorreta tinha 25k + caracteres!
O que eu não entendo é por que isso só acontece quando grep
tenta retornar a linha e não quando ela está processando em busca de outros padrões.
--null-data
podem ser úteis seNUL
é o delimitador.