Come posso grep per tutti i caratteri non ASCII?


359

Ho diversi file XML di grandi dimensioni e sto cercando di trovare le righe che contengono caratteri non ASCII. Ho provato quanto segue:

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

Ma questo restituisce ogni riga nel file, indipendentemente dal fatto che la riga contenga un carattere nell'intervallo specificato.

Ho la sintassi sbagliata o sto facendo qualcos'altro che non va? Ho anche provato:

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

(con virgolette singole e doppie che circondano il modello).


I caratteri ASCII sono lunghi solo un byte, quindi a meno che il file non sia Unicode non dovrebbero esserci caratteri sopra 0xFF.
zdav,

Come andiamo sopra \ xFF? Grep dà un errore "grep: range out of order in character class".
Mudit Jain,

Risposte:


494

Puoi usare il comando:

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

Questo ti darà il numero di riga ed evidenzierà i caratteri non ascii in rosso.

In alcuni sistemi, a seconda delle impostazioni, quanto sopra non funzionerà, quindi puoi grep dall'inverso

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

Nota anche che il bit importante è la -Pbandiera che equivale a --perl-regexp: quindi interpreterà il tuo modello come un'espressione regolare Perl. Lo dice anche quello

questo è altamente sperimentale e grep -P può avvertire di funzionalità non implementate.


42
Questo non funzionerà in BSD grep(su OS X 10.8 Mountain Lion), in quanto non supporta l' Popzione.
Bastiaan M. van de Weerd,

20
Per aggiornare il mio ultimo commento, la versione GNU di grepè disponibile nella dupeslibreria di Homebrew (abilita l'uso brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd,

48
@BastiaanVanDeWeerd è corretto, grep su OSX 10.8 non supporta più PCRE ("espressioni regolari compatibili con Perl") poiché Darwin ora utilizza BSD grep anziché GNU grep. Un'alternativa all'installazione della dupeslibreria è quella di installare pcreinvece: brew install pcre... come parte di questo, otterrai l' pcregreputilità, che puoi usare come segue:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk

15
Per gli brewutenti Mac , è possibile installare i coreutils di GNU con brew install coreutils. Questo ti darà un sacco di strumenti GNU con il prefisso 'g' - in questo caso usa ggrep. Ciò dovrebbe evitare problemi derivanti dalla sostituzione di un'utilità di sistema, poiché gli script Mac specifici del sistema ora dipendono da BSD grep.
Joel Purra,

22
funziona perfettamente su un Mac che ag "[\x80-\xFF]" filedevi solo installarethe_silver_searcher
slf

123

Invece di fare ipotesi sull'intervallo di byte di caratteri non ASCII, come fanno la maggior parte delle soluzioni di cui sopra, è invece meglio IMO essere esplicito sull'intervallo di byte effettivo di caratteri ASCII.

Quindi la prima soluzione per esempio diventerebbe:

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

(che in sostanza insegue per qualsiasi personaggio al di fuori dell'intervallo esadecimale ASCII: da \ x00 fino a \ x7F)

Su Mountain Lion che non funzionerà (a causa della mancanza del supporto PCRE in BSD grep) , ma con l' pcreinstallazione tramite Homebrew, funzionerà altrettanto bene:

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

Qualche pro o contro a cui qualcuno può pensare?


9
Questo in realtà ha funzionato per me laddove le soluzioni di cui sopra sono fallite. Trovare apostrofi M $ Word non è stato più facile!
AlbertEngelB,

2
Se si dispone di una shell compatibile con bash ma non funziona pcre-grep, LC_COLLATE=C grep $'[^\1-\177]'funziona (per file senza byte null)
idupree,

2
Questa soluzione sembra funzionare in modo più coerente rispetto a quelle sopra.
0xcaff,

1
Ho dovuto usarlo per raccogliere Kanji, cirillico e cinese tradizionale nel mio file UTF8, usando "[\ x80- \ xFF]" mancando tutti questi.
buckaroo1177125

1
Il pro è che ha funzionato in modo eccellente mentre le altre opzioni erano fantastiche ma non altrettanto grandi. Nessun contro trovato finora.
jwpfox,

67

Per me funziona quanto segue:

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

I caratteri non ASCII iniziano da 0x80 e vanno a 0xFF quando si guardano i byte. Grep (e la famiglia) non eseguono l'elaborazione Unicode per unire caratteri multibyte in una singola entità per la corrispondenza regex come sembra volere. L' -Popzione nel mio grep consente l'uso di \xddescape nelle classi di caratteri per ottenere ciò che desideri.


1
Per la vista che potrebbe non sapere immediatamente come chiamare questo su più file, basta eseguire: trova. -name * .xml | xargs grep -P "[\ x80- \ xFF]"
David Mohundro,

1
Questo restituisce una partita, ma non vi è alcuna indicazione di ciò che il personaggio è e dove si trova. Come si vede qual è il personaggio e dove si trova?
Faheem Mitha,

Aggiungendo "-n" verrà indicato il numero di riga, inoltre i caratteri non visibili verranno visualizzati come un blocco al terminale: grep -n -P "[\ x80- \ xFF]" file.xml
fooMonster

4
Sto riscontrando un problema con Hangul coreano: echo '소녀시대' | grep -P "[\x80-\xFF]"non restituisce nulla per me - qualcun altro può confermare? (GNU grep 2.21)
frabjous

@frabjous Stesso qui, ma grep le opere inverse: echo '소녀시대' | grep -P "[^\x00-\x7F]". O semplicemente usa the_silver_searchercome sottolineato da @slf:echo '소녀시대' | ag "[\x80-\xFF]"
psmith il

55

In perl

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

1
Su OSX10.11 ho dovuto provare diverse soluzioni grep + regex prima di trovare questo che funziona davvero
sg

Vuoi condividere quella soluzione OSX @sg ?!
geoteca il

Lo script perl sopra è la soluzione di cui sto parlando
sg

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

43

Il modo più semplice è definire un carattere non ASCII ... come un carattere che non è un carattere ASCII.

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

Aggiungi una scheda dopo il ^se necessario.

L'impostazione LC_COLLATE=Cevita brutte sorprese sul significato delle gamme di caratteri in molti luoghi. L'impostazione LC_CTYPE=Cè necessaria per abbinare i caratteri a byte singolo, altrimenti il ​​comando perderebbe sequenze di byte non valide nella codifica corrente. L'impostazione LC_ALL=Cevita del tutto gli effetti dipendenti dalla locale.


Su RedHat 6.4 con tcsh, ho dovuto usare <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. Ho aggiunto -n per ottenere il numero di riga.
ddevienne,

Per me echo "A" | LC_COLLATE=C grep '[^ -~]'ritorna una partita
frabjous

1
@frabjous Se lo hai LC_ALL=en_US.UTF-8, questo supera l' LC_COLLATEimpostazione. Non dovresti avere questo nel tuo ambiente! LC_ALLconsiste solo nel forzare un'attività specifica a utilizzare una determinata locale, di solito C. Per impostare le impostazioni internazionali predefinite per tutte le categorie, impostare LANG.
Gilles 'SO- smetti di essere cattivo' il

1
All'inizio, non ho aggiunto LC_ALL=C, si comporta diversamente su Mac OS X e Ubuntu. Dopo aver aggiunto questa impostazione, danno lo stesso risultato.
Max Peng,

1
Funziona su un Mac, mentre le altre soluzioni basate su grep no.
Matthias Fripp,

26

Ecco un'altra variante che ho trovato che ha prodotto risultati completamente diversi dalla ricerca grep [\x80-\xFF]nella risposta accettata. Forse sarà utile a qualcuno trovare altri caratteri non ascii:

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

Nota: grep (un Mac) del mio computer non aveva -Popzioni, quindi l'ho fatto brew install grepe ho iniziato la chiamata sopra con ggrepinvece di grep.


2
Questa è di gran lunga la risposta migliore, poiché funziona per Mac e Linux.
tommy.carstensen,

Solo uno che ha funzionato per me su Linux.

9

Il seguente codice funziona:

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

Sostituisci /tmpcon il nome della directory in cui vuoi cercare.


2
Su un Mac, questo funziona, mentre la maggior parte di quelli basati su grep no.
Matthias Fripp,

9

Ricerca di caratteri non stampabili. TLDR; Sintesi

  1. cerca caratteri di controllo E Unicode esteso
  2. impostazioni locali, ad es. LC_ALL=Cnecessarie per fare in modo che grep faccia ciò che ci si potrebbe aspettare con un Unicode esteso

Quindi i cercatori di caratteri non ascii preferiti:

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

come nella risposta migliore, il grep inverso:

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

come nella risposta migliore ma CON LC_ALL=C:

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

. . Di Più . . dettagli lancinanti su questo:. . .

Concordo con Harvey sopra seppellito nei commenti, è spesso più utile cercare caratteri non stampabili O è facile pensare non ASCII quando si dovrebbe davvero pensare non stampabile. Harvey suggerisce "usa questo:" [^\n -~]". Aggiungi \ r per i file di testo DOS. Ciò si traduce in" [^\x0A\x020-\x07E]"e aggiungi \ x0D per CR"

Inoltre, aggiungere -c (mostra il conteggio dei modelli abbinati) a grep è utile quando si cercano caratteri non stampabili poiché le stringhe abbinate possono rovinare il terminale.

Ho trovato che aggiungere l'intervallo 0-8 e 0x0e-0x1f (all'intervallo 0x80-0xff) è un modello utile. Ciò esclude TAB, CR e LF e uno o due caratteri stampabili non comuni. Quindi IMHO un modello grep abbastanza utile (anche se grezzo) è QUESTO:

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

REALMENTE, generalmente dovrai fare questo:

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

abbattersi:

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

Ad esempio, un esempio pratico di utilizzo trova per grep tutti i file nella directory corrente:

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

Potresti voler regolare il grep a volte. ad es. carattere BS (0x08 - backspace) utilizzato in alcuni file stampabili o per escludere VT (0x0B - scheda verticale). I caratteri BEL (0x07) e ESC (0x1B) possono anche essere considerati stampabili in alcuni casi.

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

AGGIORNAMENTO: ho dovuto rivisitare questo di recente. E, YYMV a seconda delle impostazioni del terminale / previsioni meteorologiche solari MA. . Ho notato che grep non stava trovando molti caratteri Unicode o estesi. Anche se intuitivamente dovrebbero corrispondere all'intervallo da 0x80 a 0xff, i caratteri unicode a 3 e 4 byte non sono stati abbinati. ??? Qualcuno può spiegare questo? SÌ. @frabjous ha chiesto e @calandoa ha spiegato che LC_ALL=Cdovrebbe essere usato per impostare le impostazioni locali per il comando per far corrispondere grep.

ad es. il mio locale LC_ALL=vuoto

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

grep con LC_ALL=vuoti corrisponde a caratteri codificati a 2 byte ma non a 3 e 4 byte codificati:

$ 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=Csembra corrispondere a tutti i personaggi estesi che vorresti:

$ 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

QUESTA corrispondenza del perl (parzialmente trovata altrove su StackOverflow) OPPURE il grep inverso nella risposta principale sembra trovare TUTTI i caratteri ~ bizzarri ~ e ~ meravigliosi "non ascii" senza impostare le impostazioni locali:

$ 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

Quindi i cercatori di caratteri non ascii preferiti:

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

come nella risposta migliore, il grep inverso:

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

come nella risposta migliore ma CON LC_ALL=C:

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

1
Rispondi al motivo per cui grep non corrisponde ai caratteri codificati in più di 2 byte grazie a @calandoa e frabjous nei commenti sopra sulla domanda. Utilizzare LC_ALL = C prima del comando grep.
sabato

1
Grazie mille per il disturbo di pubblicare una risposta sepolta sotto altri 800 voti! Il mio problema era un carattere 0x02. Potresti mettere questo "esempio pratico di utilizzo" in alto, dato che non hai davvero bisogno di leggere l'intero post per vedere se questo è il tuo problema.
Noumenon,

1
Lo so, una risposta davvero vecchia e dettagli lancinanti, ma spero utile anche per me e per gli altri. Hai ragione, ho aggiunto TLDR; in cima.
gaoithe,

1

Stranamente, ho dovuto farlo oggi! Ho finito per usare Perl perché non riuscivo a far funzionare grep / egrep (anche in modalità -P). Qualcosa di simile a:

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

Per i caratteri Unicode (come \u2212nell'esempio seguente) utilizzare questo:

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

Potrebbe essere interessante sapere come cercare un personaggio Unicode. Questo comando può aiutare. Devi solo conoscere il codice in UTF8

grep -v $'\u200d'

Non sono davvero un esperto, ma so abbastanza per sapere che non è una rappresentazione UTF8, è UTF16, o forse UTF32 o UCS16. Per un punto di codice a 2 byte, questi tre potrebbero essere tutti uguali.
Baxissimo,

1

La ricerca di tutti i caratteri non ascii dà l'impressione che si stiano cercando stringhe unicode o che si desideri eliminare singolarmente tali caratteri.

Per il primo, prova uno di questi (la variabile fileviene utilizzata per l'automazione):

 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

Vanilla grep non funziona correttamente senza LC_ALL = C, come indicato nelle risposte precedenti.

L'intervallo ASCII è x00-x7F, lo spazio è x20, poiché le stringhe hanno spazi l'intervallo negativo lo omette.

L'intervallo non ASCII è x80-xFF, poiché le stringhe hanno spazi l'intervallo positivo lo aggiunge.

Si presume che la stringa contenga almeno 7 caratteri consecutivi all'interno dell'intervallo. {7,}.

Per l'output leggibile dalla shell, uchardet $filerestituisce un'ipotesi della codifica del file che viene passata a iconv per l'interpolazione automatica.


Questo è molto utile grazie alla menzione del uchardetcomando. Grazie per l'heads-up!
bballdave025,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.