Como faço para grep para todos os caracteres não ASCII?


359

Eu tenho vários arquivos XML muito grandes e estou tentando encontrar as linhas que contêm caracteres não ASCII. Eu tentei o seguinte:

grep -e "[\x{00FF}-\x{FFFF}]" file.xml

Mas isso retorna todas as linhas do arquivo, independentemente de a linha conter um caractere no intervalo especificado.

Tenho a sintaxe errada ou estou fazendo outra coisa errada? Eu também tentei:

egrep "[\x{00FF}-\x{FFFF}]" file.xml 

(com aspas simples e duplas ao redor do padrão).


Os caracteres ASCII têm apenas um byte, portanto, a menos que o arquivo seja unicode, não deve haver caracteres acima de 0xFF.
zdav

Como vamos acima de \ xFF? O Grep fornece um erro "grep: intervalo fora de ordem na classe de caracteres".
Mudit Jain

Respostas:


494

Você pode usar o comando:

grep --color='auto' -P -n "[\x80-\xFF]" file.xml

Isso fornecerá o número da linha e destacará caracteres não-ascii em vermelho.

Em alguns sistemas, dependendo das suas configurações, as opções acima não funcionarão, portanto, você pode esperar pelo inverso

grep --color='auto' -P -n "[^\x00-\x7F]" file.xml

Observe também que o bit importante é a -Pflag que equivale a --perl-regexp: portanto, ele interpretará seu padrão como uma expressão regular do Perl. Também diz que

isso é altamente experimental e o grep -P pode avisar sobre recursos não implementados.


42
Isso não funcionará no BSD grep(no OS X 10.8 Mountain Lion), pois não suporta a Popção.
Bastiaan M. van de Weerd

20
Para atualizar meu último comentário, a versão GNU de grepestá disponível na dupesbiblioteca do Homebrew (habilite o uso brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd

48
@BastiaanVanDeWeerd está correto, o grep no OSX 10.8 não é mais compatível com PCRE ("expressões regulares compatíveis com Perl"), já que o Darwin agora usa o BSD grep em vez do GNU grep. Uma alternativa para instalar a dupesbiblioteca é instalar pcre: brew install pcre... como parte disso, você obterá o pcregreputilitário, que pode ser usado da seguinte forma:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk

15
Para brewusuários de Mac , os coreutils do GNU podem ser instalados com brew install coreutils. Isso lhe dará muitas ferramentas GNU prefixadas com um 'g' - neste caso, use ggrep. Isso deve evitar problemas decorrentes da substituição de um utilitário do sistema, pois os scripts Mac específicos do sistema agora dependem do BSD grep.
Joel Purra

22
esta multa funciona em um Mac ag "[\x80-\xFF]" filevocê só precisa instalarthe_silver_searcher
SLF

123

Em vez de fazer suposições sobre o intervalo de bytes de caracteres não ASCII, como a maioria das soluções acima, é um pouco melhor que o IMO seja explícito sobre o intervalo real de bytes de caracteres ASCII.

Portanto, a primeira solução, por exemplo, seria:

grep --color='auto' -P -n '[^\x00-\x7F]' file.xml

(que basicamente espera por qualquer caractere fora do intervalo ASCII hexadecimal: de \ x00 a \ x7F)

No Mountain Lion que não funciona (devido à falta de suporte do PCRE no BSD grep) , mas com o pcreinstalado via Homebrew, o seguinte também funciona:

pcregrep --color='auto' -n '[^\x00-\x7F]' file.xml

Quaisquer prós ou contras que alguém possa pensar?


9
Isso realmente funcionou para mim, onde as soluções acima falharam. Encontrar apóstrofos do M $ Word não foi mais fácil!
AlbertEngelB

2
Se você tem um shell bash-compatível, mas trabalho não pcre-grep, LC_COLLATE=C grep $'[^\1-\177]'obras (para arquivos sem nula bytes)
idupree

2
Essa solução parece funcionar de forma mais consistente do que as anteriores.
0xcaff

11
Eu tive que usar isso para pegar kanji, cirílico e chinês tradicional no meu arquivo UTF8, usando "[\ x80- \ xFF]" perdeu tudo isso.
buckaroo1177125

11
O profissional é que isso funcionou de forma excelente, enquanto as outras opções eram ótimas, mas não tão boas. Nenhum contras encontrado até agora.
Jwpfox

67

O seguinte funciona para mim:

grep -P "[\x80-\xFF]" file.xml

Caracteres não ASCII iniciam em 0x80 e vão para 0xFF ao olhar para bytes. O Grep (e a família) não executam o processamento Unicode para mesclar caracteres de vários bytes em uma única entidade para a correspondência de expressões regulares como você deseja. A -Popção no meu grep permite o uso de \xddescapes nas classes de caracteres para realizar o que você deseja.


11
Para a visualização que pode não saber imediatamente como chamar isso em vários arquivos, basta executar: find. -name * .xml | xargs grep -P "[\ x80- \ xFF]"
David Mohundro

11
Isso retorna uma partida, mas não há indicação de qual é o personagem e onde ele está. Como alguém vê qual é o personagem e onde ele está?
Faheem Mitha

Adicionar o "-n" fornecerá o número da linha, além disso, caracteres não visíveis aparecerão como um bloco no terminal: grep -n -P "[\ x80- \ xFF]" file.xml
fooMonster

4
Estou tendo um problema com o Hangul Korean: echo '소녀시대' | grep -P "[\x80-\xFF]"não retorna nada para mim - mais alguém pode confirmar? (GNU grep 2.21)
frabjous

@frabjous Mesmo aqui, mas grepping as obras inversas: echo '소녀시대' | grep -P "[^\x00-\x7F]". Ou simplesmente usar the_silver_searchercomo fora apontado por @slf:echo '소녀시대' | ag "[\x80-\xFF]"
psmith

55

Em perl

perl -ane '{ if(m/[[:^ascii:]]/) { print  } }' fileName > newFile

11
Em OSX10.11 eu tinha que tentar várias grep + regex soluções antes de encontrar este que realmente funciona
sg

Gostaria de compartilhar essa solução OSX @sg ?!
Geotheory

O script perl acima é a solução que eu estou falando
sg

5
perl -lne 'print if /[^[:ascii:]]/' file.xml
Naveed

43

A maneira mais fácil é definir um caractere não ASCII ... como um caractere que não é um caractere ASCII.

LC_ALL=C grep '[^ -~]' file.xml

Adicione uma guia após o ^se necessário.

A configuração LC_COLLATE=Cevita surpresas desagradáveis ​​sobre o significado dos intervalos de caracteres em muitos locais. A configuração LC_CTYPE=Cé necessária para corresponder caracteres de byte único - caso contrário, o comando perderia seqüências de bytes inválidas na codificação atual. A configuração LC_ALL=Cevita completamente os efeitos dependentes da localidade.


No RedHat 6.4 com tcsh, eu tive que usar <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. Adicionei -n para obter o número da linha.
ddevienne

Para mim, echo "A" | LC_COLLATE=C grep '[^ -~]'retorna uma partida
frabjous

11
@frabjous Se você tiver LC_ALL=en_US.UTF-8, isso supera a LC_COLLATEconfiguração. Você não deve ter isso em seu ambiente! LC_ALLé apenas forçar uma tarefa específica a usar um código de idioma específico, geralmente C. Para definir o código do idioma padrão para todas as categorias, defina LANG.
Gilles 'SO- stop be evil'

11
No começo, eu não adicionei LC_ALL=C, ele se comporta de maneira diferente no Mac OS X e no Ubuntu. Depois de adicionar essa configuração, eles dão o mesmo resultado.
Max Peng

11
Isso funciona em um Mac, enquanto as outras soluções baseadas em grep não.
Matthias Fripp #

26

Aqui está outra variante que achei que produziu resultados completamente diferentes da pesquisa grep [\x80-\xFF]na resposta aceita. Talvez seja útil para alguém encontrar caracteres não-ascii adicionais:

grep --color='auto' -P -n "[^[:ascii:]]" myfile.txt

Nota: o grep do meu computador (um Mac) não tinha -Popção, então eu fiz brew install grepe iniciei a chamada acima com em ggrepvez de grep.


2
Essa é de longe a melhor resposta, pois funciona tanto para Mac quanto para Linux.
tommy.carstensen

Único que funcionou para mim no Linux.

9

O código a seguir funciona:

find /tmp | perl -ne 'print if /[^[:ascii:]]/'

Substitua /tmppelo nome do diretório que você deseja pesquisar.


2
Em um Mac, isso funciona, enquanto a maioria dos baseados em grep não.
Matthias Fripp #

9

Procurando caracteres não imprimíveis. TLDR; Sumário executivo

  1. procure caracteres de controle E unicode estendido
  2. configuração de localidade, por exemplo, LC_ALL=Cnecessária para fazer o grep fazer o que você poderia esperar com o Unicode estendido

SO os localizadores de char não-ASCII preferidos:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

como na resposta principal, o grep inverso:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

como na resposta principal, mas COM LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

. . Mais . . detalhes excruciantes sobre isso:. . .

Eu concordo com Harvey acima, enterrado nos comentários, geralmente é mais útil procurar caracteres não imprimíveis OU é fácil pensar em não ASCII quando você realmente deveria estar pensando em não imprimível. Harvey sugere "use isto:" [^\n -~]". Adicione \ r para arquivos de texto do DOS. Isso se traduz em" [^\x0A\x020-\x07E]"e adicione \ x0D para CR"

Além disso, adicionar -c (mostrar número de padrões correspondentes) ao grep é útil ao procurar por caracteres não imprimíveis, pois as cadeias correspondentes podem atrapalhar o terminal.

Eu achei que adicionar intervalo 0-8 e 0x0e-0x1f (ao intervalo 0x80-0xff) é um padrão útil. Isso exclui o TAB, CR e LF e mais um ou dois caracteres imprimíveis incomuns. Portanto, o IMHO, um padrão grep bastante útil (embora bruto), é ESTE:

grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

Na verdade, geralmente você precisará fazer isso:

LC_ALL=C grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" *

demolir:

LC_ALL=C - set locale to C, otherwise many extended chars will not match (even though they look like they are encoded > 0x80)
\x00-\x08 - non-printable control chars 0 - 7 decimal
\x0E-\x1F - more non-printable control chars 14 - 31 decimal
\x80-1xFF - non-printable chars > 128 decimal
-c - print count of matching lines instead of lines
-P - perl style regexps

Instead of -c you may prefer to use -n (and optionally -b) or -l
-n, --line-number
-b, --byte-offset
-l, --files-with-matches

Por exemplo, exemplo prático de uso find para grep todos os arquivos no diretório atual:

LC_ALL=C find . -type f -exec grep -c -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" {} + 

Você pode ajustar o grep às vezes. por exemplo, caractere BS (0x08 - backspace) usado em alguns arquivos imprimíveis ou para excluir VT (0x0B - guia vertical). Os caracteres BEL (0x07) e ESC (0x1B) também podem ser considerados imprimíveis em alguns casos.

Non-Printable ASCII Chars
** marks PRINTABLE but CONTROL chars that is useful to exclude sometimes
Dec   Hex Ctrl Char description           Dec Hex Ctrl Char description
0     00  ^@  NULL                        16  10  ^P  DATA LINK ESCAPE (DLE)
1     01  ^A  START OF HEADING (SOH)      17  11  ^Q  DEVICE CONTROL 1 (DC1)
2     02  ^B  START OF TEXT (STX)         18  12  ^R  DEVICE CONTROL 2 (DC2)
3     03  ^C  END OF TEXT (ETX)           19  13  ^S  DEVICE CONTROL 3 (DC3)
4     04  ^D  END OF TRANSMISSION (EOT)   20  14  ^T  DEVICE CONTROL 4 (DC4)
5     05  ^E  END OF QUERY (ENQ)          21  15  ^U  NEGATIVE ACKNOWLEDGEMENT (NAK)
6     06  ^F  ACKNOWLEDGE (ACK)           22  16  ^V  SYNCHRONIZE (SYN)
7     07  ^G  BEEP (BEL)                  23  17  ^W  END OF TRANSMISSION BLOCK (ETB)
8     08  ^H  BACKSPACE (BS)**            24  18  ^X  CANCEL (CAN)
9     09  ^I  HORIZONTAL TAB (HT)**       25  19  ^Y  END OF MEDIUM (EM)
10    0A  ^J  LINE FEED (LF)**            26  1A  ^Z  SUBSTITUTE (SUB)
11    0B  ^K  VERTICAL TAB (VT)**         27  1B  ^[  ESCAPE (ESC)
12    0C  ^L  FF (FORM FEED)**            28  1C  ^\  FILE SEPARATOR (FS) RIGHT ARROW
13    0D  ^M  CR (CARRIAGE RETURN)**      29  1D  ^]  GROUP SEPARATOR (GS) LEFT ARROW
14    0E  ^N  SO (SHIFT OUT)              30  1E  ^^  RECORD SEPARATOR (RS) UP ARROW
15    0F  ^O  SI (SHIFT IN)               31  1F  ^_  UNIT SEPARATOR (US) DOWN ARROW

ATUALIZAÇÃO: Eu tive que revisitar isso recentemente. E, YYMV, dependendo das configurações do terminal / previsão do tempo solar, MAS. . Percebi que o grep não estava encontrando muitos caracteres unicode ou estendidos. Embora intuitivamente eles correspondam ao intervalo de 0x80 a 0xff, os caracteres unicode de 3 e 4 bytes não foram correspondidos. ??? Alguém pode explicar isso? SIM. O @frabjous perguntou e o @calandoa explicou que LC_ALL=Cdeve ser usado para definir o código do idioma para o comando fazer a correspondência grep.

por exemplo, meu local LC_ALL=vazio

$ locale
LANG=en_IE.UTF-8
LC_CTYPE="en_IE.UTF-8"
.
.
LC_ALL=

grep com LC_ALL=correspondências vazias caracteres codificados em 2 bytes, mas não em 3 e 4 bytes:

$ grep -P -n "[\x00-\x08\x0E-\x1F\x80-\xFF]" notes_unicode_emoji_test
5 copyright c2a9
7:call  underscore c2a0
9:CTRL
31:5 © copyright
32:7 call  underscore

grep with LC_ALL=Cparece corresponder a todos os caracteres estendidos que você deseja:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test  
1:���� unicode dashes e28090
3:��� Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5:� copyright c2a9
7:call underscore c2a0
11:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29:1 ���� unicode dashes
30:3 ��� Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31:5  copyright
32:7 call underscore
33:11 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
34:52 LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other
81:LIVE��E! ���������� ���� ���������� ���� �� �� ���� ����  YEOW, mix of japanese and chars from other

ESTA correspondência perl (parcialmente encontrada em outro lugar no stackoverflow) OU o grep inverso na resposta superior parecem encontrar TODOS os caracteres "estranhos" e ~ maravilhosos ~ "não-ascii" estranhos e sem definição de localidade:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test  

1 ‐‐ unicode dashes e28090
3 💘 Heart With Arrow Emoji - Emojipedia == UTF8? f09f9298
5 © copyright c2a9
7 call  underscore c2a0
9 CTRL-H CHARS URK URK URK 
11 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other e38182 e38184 . . e0a487
29 1 ‐‐ unicode dashes
30 3 💘 Heart With Arrow Emoji - Emojipedia == UTF8 e28090
31 5 © copyright
32 7 call  underscore
33 11 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
34 52 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other
73 LIVEE! あいうえお かが アイウエオ カガ   ซฌ आइ  YEOW, mix of japanese and chars from other

SO os localizadores de char não-ASCII preferidos:

$ perl -ne 'print "$. $_" if m/[\x00-\x08\x0E-\x1F\x80-\xFF]/' notes_unicode_emoji_test

como na resposta principal, o grep inverso:

$ grep --color='auto' -P -n "[^\x00-\x7F]" notes_unicode_emoji_test

como na resposta principal, mas COM LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test

11
Responda por que o grep não corresponde aos caracteres codificados em mais de 2 bytes, graças a @calandoa e frabjous nos comentários acima na pergunta. Use LC_ALL = C antes do comando grep.
precisa saber é

11
Muito obrigado por se incomodar em postar uma resposta escondida sob 800 outros votos positivos! Meu problema era um caractere 0x02. Você pode colocar esse "exemplo prático de uso" próximo ao topo, pois você realmente não precisa ler o post inteiro para ver se esse é o seu problema.
Noumenon

11
Eu sei, resposta realmente antiga e detalhes excrutadores, mas correcto e útil para mim e para outros também espero. Você está certo, eu adicionei TLDR; no topo.
precisa saber é o seguinte

1

Estranhamente, eu tive que fazer isso hoje! Acabei usando o Perl porque não consegui fazer o grep / egrep funcionar (mesmo no modo -P). Algo como:

cat blah | perl -en '/\xCA\xFE\xBA\xBE/ && print "found"'

Para caracteres unicode (como \u2212no exemplo abaixo), use o seguinte:

find . ... -exec perl -CA -e '$ARGV = @ARGV[0]; open IN, $ARGV; binmode(IN, ":utf8"); binmode(STDOUT, ":utf8"); while (<IN>) { next unless /\N{U+2212}/; print "$ARGV: $&: $_"; exit }' '{}' \;

1

Pode ser interessante saber como procurar um caractere unicode. Este comando pode ajudar. Você só precisa saber o código em UTF8

grep -v $'\u200d'

Não sou realmente um especialista, mas sei o suficiente para saber que não é uma representação UTF8, é UTF16, ou talvez UTF32, ou UCS16. Para um ponto de código de 2 bytes, esses três podem ser todos iguais.
Baxissimo 11/04

1

Encontrar todos os caracteres não-ascii dá a impressão de que alguém está procurando por seqüências unicode ou pretende extrair esses caracteres individualmente.

Para o primeiro, tente um destes (a variável fileé usada para automação):

 file=file.txt ; LC_ALL=C grep -Piao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[\x80-\xFF\x20]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

 file=file.txt ; pcregrep -iao '[^\x00-\x19\x21-\x7F]{7,}' $file | iconv -f $(uchardet $file) -t utf-8

O grep de baunilha não funciona corretamente sem LC_ALL = C, conforme observado nas respostas anteriores.

O intervalo ASCII é x00-x7F, o espaço é x20, pois as cadeias possuem espaços que o intervalo negativo o omite.

O intervalo não-ASCII é que x80-xFF, como as strings têm espaços, o intervalo positivo o adiciona.

Presume-se que a sequência tenha pelo menos 7 caracteres consecutivos dentro do intervalo. {7,}.

Para saída legível por shell, uchardet $fileretorna uma estimativa da codificação do arquivo que é passada para iconv para interpolação automática.


Isso é muito útil devido à menção do uchardetcomando. Obrigado por esse aviso!
bballdave025
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.