Comment est-ce que je grep pour tous les caractères non-ASCII?

359

J'ai plusieurs fichiers XML très volumineux et j'essaie de trouver les lignes qui contiennent des caractères non ASCII. J'ai essayé ce qui suit:

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

Mais cela renvoie toutes les lignes du fichier, que la ligne contienne ou non un caractère dans la plage spécifiée.

La syntaxe est-elle incorrecte ou est-ce que je fais autre chose de mal? J'ai aussi essayé:

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

(avec des guillemets simples et doubles entourant le motif).

pconrey
la source
Les caractères ASCII ne font qu'un octet, donc à moins que le fichier ne soit unicode, il ne devrait pas y avoir de caractères au-dessus de 0xFF.
zdav
Comment allons-nous au-dessus de \ xFF? Grep donne une erreur "grep: range out of order in character class".
Mudit Jain

Réponses:

495

Vous pouvez utiliser la commande:

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

Cela vous donnera le numéro de ligne et mettra en évidence les caractères non ascii en rouge.

Dans certains systèmes, en fonction de vos paramètres, ce qui précède ne fonctionnera pas, vous pouvez donc grep par l'inverse

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

Notez également que le bit important est le -Pdrapeau qui équivaut à --perl-regexp: il interprétera donc votre modèle comme une expression régulière Perl. Il dit également que

ceci est hautement expérimental et grep -P peut avertir des fonctionnalités non implémentées.

jerrymouse
la source
42
Cela ne fonctionnera pas dans BSD grep(sur OS X 10.8 Mountain Lion), car il ne prend pas en charge l' Poption.
Bastiaan M. van de Weerd
20
Pour mettre à jour mon dernier commentaire, la version GNU de grepest disponible dans la dupesbibliothèque de Homebrew (activer à l'aide brew tap homebrew/dupes):brew install grep
Bastiaan M. van de Weerd
48
@BastiaanVanDeWeerd est correct, grep sur OSX 10.8 ne prend plus en charge PCRE ("expressions régulières compatibles Perl") car Darwin utilise désormais grep BSD au lieu de grep GNU. Une alternative à l'installation de la dupesbibliothèque est d'installer à la pcreplace: brew install pcre... dans le cadre de cela, vous obtiendrez l' pcregreputilitaire, que vous pouvez utiliser comme suit:pcregrep --color='auto' -n "[\x80-\xFF]" file.xml
pvandenberk
15
Pour les brewutilisateurs de Mac , les coreutils de GNU peuvent être installés avec brew install coreutils. Cela vous donnera beaucoup d'outils GNU préfixés par un «g» - dans ce cas, utilisez ggrep. Cela devrait éviter les problèmes liés au remplacement d'un utilitaire système, car les scripts Mac spécifiques au système dépendent désormais de grep BSD.
Joel Purra
22
cela fonctionne très bien sur un mac que ag "[\x80-\xFF]" filevous avez juste besoin d'installerthe_silver_searcher
slf
123

Au lieu de faire des hypothèses sur la plage d'octets des caractères non ASCII, comme le font la plupart des solutions ci-dessus, il est légèrement préférable que l'OMI soit explicite sur la plage d'octets réelle des caractères ASCII.

Ainsi, la première solution par exemple deviendrait:

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

(qui recherche essentiellement tout caractère en dehors de la plage ASCII hexadécimale: de \ x00 à \ x7F)

Sur Mountain Lion, cela ne fonctionnera pas (en raison du manque de prise en charge PCRE dans BSD grep) , mais avec pcreinstallé via Homebrew, les éléments suivants fonctionneront tout aussi bien:

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

Des avantages ou des inconvénients auxquels tout le monde peut penser?

pvandenberk
la source
9
Cela a réellement fonctionné pour moi là où les solutions ci-dessus ont échoué. Trouver des apostrophes M $ Word n'a pas été plus facile!
AlbertEngelB
2
Si vous avez un shell compatible bash mais que pcre-grep ne fonctionne pas, LC_COLLATE=C grep $'[^\1-\177]'fonctionne (pour les fichiers sans octets nuls)
idupree
2
Cette solution semble fonctionner de manière plus cohérente que celles ci-dessus.
0xcaff
1
J'ai dû l'utiliser pour récupérer le kanji, le cyrillique et le chinois traditionnel dans mon fichier UTF8, en utilisant "[\ x80- \ xFF]", j'ai manqué tout cela.
buckaroo1177125
1
Le pro est que cela a fonctionné très bien tandis que les autres options étaient super mais pas aussi bien. Aucun inconvénient trouvé jusqu'à présent.
jwpfox
67

Ce qui suit fonctionne pour moi:

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

Les caractères non ASCII commencent à 0x80 et vont à 0xFF lorsque vous regardez les octets. Grep (et la famille) ne fait pas de traitement Unicode pour fusionner des caractères multi-octets en une seule entité pour la correspondance regex comme vous semblez le vouloir. L' -Poption dans mon grep permet d'utiliser des \xddéchappements dans les classes de caractères pour accomplir ce que vous voulez.

Thelema
la source
1
Pour la vue qui ne sait pas immédiatement comment appeler cela sur plusieurs fichiers, exécutez simplement: find. -name * .xml | xargs grep -P "[\ x80- \ xFF]"
David Mohundro
1
Cela retourne une correspondance, mais il n'y a aucune indication de ce qu'est le personnage et où il se trouve. Comment voit-on le personnage et où il se trouve?
Faheem Mitha
L'ajout de "-n" donnera le numéro de ligne, en plus les caractères non visibles s'afficheront sous forme de bloc sur le terminal: grep -n -P "[\ x80- \ xFF]" file.xml
fooMonster
4
J'ai un problème avec Hangul Korean: echo '소녀시대' | grep -P "[\x80-\xFF]"je ne retourne rien pour moi - quelqu'un d'autre peut-il confirmer? (GNU grep 2.21)
frabjous
@frabjous même, mais grepper les travaux inverses: echo '소녀시대' | grep -P "[^\x00-\x7F]". Ou utilisez simplement the_silver_searchercomme indiqué par @slf:echo '소녀시대' | ag "[\x80-\xFF]"
psmith
55

En perl

perl -ane '{ if(m/[[:^ascii:]]/) { print  } }' fileName > newFile
noquery
la source
1
Sur OSX10.11, j'ai dû essayer plusieurs solutions grep + regex avant de trouver celle qui fonctionne réellement
sg
Vous souhaitez partager cette solution OSX @sg?!
geotheory
Le script Perl ci-dessus est la solution dont je parle
sg
5
perl -lne 'print if /[^[:ascii:]]/' file.xml
Naveed le
43

Le moyen le plus simple est de définir un caractère non ASCII ... comme un caractère qui n'est pas un caractère ASCII.

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

Ajoutez un onglet après le ^si nécessaire.

Le réglage LC_COLLATE=Cévite les mauvaises surprises sur la signification des plages de caractères dans de nombreux paramètres régionaux. Le réglage LC_CTYPE=Cest nécessaire pour faire correspondre les caractères à un octet - sinon la commande manquerait des séquences d'octets invalides dans le codage actuel. Le réglage LC_ALL=Cévite complètement les effets locaux.

Gilles 'SO- arrête d'être méchant'
la source
Sur RedHat 6.4 avec tcsh, j'ai dû utiliser <<< env LC_COLLATE = C grep -n '[^ - ~]' file.xml >>>. J'ai ajouté -n pour obtenir le numéro de ligne.
ddevienne
Pour moi echo "A" | LC_COLLATE=C grep '[^ -~]'retourne un match
frabjous
1
@frabjous Si vous l'avez LC_ALL=en_US.UTF-8, cela l'emporte sur le LC_COLLATEparamètre. Vous ne devriez pas avoir cela dans votre environnement! LC_ALLconsiste uniquement à forcer une tâche spécifique à utiliser un environnement local particulier, généralement C. Pour définir les paramètres régionaux par défaut pour toutes les catégories, définissez LANG.
Gilles 'SO- arrête d'être méchant'
1
Au début, je n'ai pas ajouté LC_ALL=C, il se comporte différemment sur Mac OS X et Ubuntu. Après avoir ajouté ce paramètre, ils donnent le même résultat.
Max Peng
1
Cela fonctionne sur un Mac, contrairement aux autres solutions basées sur grep.
Matthias Fripp
26

Voici une autre variante que j'ai trouvée qui a produit des résultats complètement différents de la recherche grep [\x80-\xFF]dans la réponse acceptée. Il sera peut-être utile à quelqu'un de trouver des caractères non ascii supplémentaires:

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

Remarque: le grep de mon ordinateur (un Mac) n'avait pas d' -Poption, donc je l'ai fait brew install grepet j'ai commencé l'appel ci-dessus avec ggrepau lieu de grep.

ryanm
la source
2
C'est de loin la meilleure réponse, car cela fonctionne pour Mac et Linux.
tommy.carstensen
Un seul qui a fonctionné pour moi sur Linux.
9

Le code suivant fonctionne:

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

Remplacez /tmppar le nom du répertoire que vous souhaitez rechercher.

user7417071
la source
2
Sur un Mac, cela fonctionne, contrairement à la plupart des applications basées sur grep.
Matthias Fripp
9

Recherche de caractères non imprimables. TLDR; Résumé

  1. recherche de caractères de contrôle ET d'unicode étendu
  2. paramètre local par exemple LC_ALL=Cnécessaire pour que grep fasse ce que vous attendez avec unicode étendu

SO les trouveurs de caractères non ascii préférés:

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

comme dans la première réponse, la grep inverse:

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

comme dans la première réponse mais AVEC LC_ALL=C:

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

. . plus . . détail atroce à ce sujet:. . .

Je suis d'accord avec Harvey ci-dessus enterré dans les commentaires, il est souvent plus utile de rechercher des caractères non imprimables OU il est facile de penser non ASCII alors que vous devriez vraiment penser non imprimable. Harvey suggère "utilisez ceci:" [^\n -~]". Ajoutez \ r pour les fichiers texte DOS. Cela se traduit par" [^\x0A\x020-\x07E]"et ajoutez \ x0D pour CR"

De plus, l'ajout de -c (afficher le nombre de modèles correspondants) à grep est utile lors de la recherche de caractères non imprimables car les chaînes correspondantes peuvent perturber le terminal.

J'ai trouvé que l'ajout de la plage 0-8 et 0x0e-0x1f (à la plage 0x80-0xff) est un modèle utile. Cela exclut le TAB, le CR et le LF et un ou deux autres caractères imprimables rares. Donc, à mon humble avis, un modèle de grep assez utile (quoique brut) est celui-ci:

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

RÉELLEMENT, vous devrez généralement faire ceci:

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

panne:

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

Exemple d'utilisation pratique de find pour grep tous les fichiers du répertoire courant:

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

Vous pouvez parfois ajuster le grep. Par exemple, le caractère BS (0x08 - backspace) utilisé dans certains fichiers imprimables ou pour exclure VT (0x0B - onglet vertical). Les caractères BEL (0x07) et ESC (0x1B) peuvent également être considérés comme imprimables dans certains cas.

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

MISE À JOUR: J'ai dû revoir cela récemment. Et, YYMV en fonction des paramètres du terminal / des prévisions météorologiques solaires MAIS. . J'ai remarqué que grep ne trouvait pas beaucoup de caractères unicode ou étendus. Même si intuitivement, ils doivent correspondre à la plage 0x80 à 0xff, les caractères unicode à 3 et 4 octets ne correspondent pas. ??? Quelqu'un peut-il expliquer cela? OUI. @frabjous a demandé et @calandoa a expliqué que cela LC_ALL=Cdevrait être utilisé pour définir les paramètres régionaux pour que la commande fasse correspondre grep.

par exemple, mes paramètres régionaux sont LC_ALL=vides

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

grep avec LC_ALL=vide correspond à des caractères codés sur 2 octets mais pas codés sur 3 et 4 octets:

$ 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=Csemble correspondre à tous les caractères étendus que vous souhaitez:

$ 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

CETTE correspondance perl (partiellement trouvée ailleurs sur stackoverflow) OU l'inverse grep sur la réponse du haut semble trouver TOUS les caractères ~ bizarre ~ et ~ merveilleux ~ "non-ascii" sans définir les paramètres régionaux:

$ 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 les trouveurs de caractères non ascii préférés:

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

comme dans la première réponse, la grep inverse:

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

comme dans la première réponse mais AVEC LC_ALL=C:

$ LC_ALL=C grep --color='auto' -P -n "[\x80-\xFF]" notes_unicode_emoji_test
gaoithe
la source
1
Répondez aux raisons pour lesquelles grep ne correspond pas aux caractères encodés sur plus de 2 octets grâce à @calandoa et frabjous dans les commentaires ci-dessus sur la question. Utilisez LC_ALL = C avant la commande grep.
gaoithe
1
Merci beaucoup d'avoir pris la peine de poster une réponse enterrée sous 800 autres votes positifs! Mon problème était un caractère 0x02. Vous voudrez peut-être mettre cet "exemple pratique d'utilisation" vers le haut, car vous n'avez vraiment pas besoin de lire l'intégralité du message pour voir si c'est votre problème.
Noumenon
1
Je sais, réponse vraiment ancienne et détails atroces, mais correct utile pour moi et pour les autres aussi j'espère. Vous avez raison, j'ai ajouté TLDR; en haut.
gaoithe
1

Étrangement, je devais le faire aujourd'hui! J'ai fini par utiliser Perl parce que je ne pouvais pas faire fonctionner grep / egrep (même en mode -P). Quelque chose comme:

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

Pour les caractères unicode (comme \u2212dans l'exemple ci-dessous), utilisez ceci:

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 }' '{}' \;
dty
la source
1

Il pourrait être intéressant de savoir comment rechercher un caractère unicode. Cette commande peut vous aider. Il vous suffit de connaître le code en UTF8

grep -v $'\u200d'
arezae
la source
Je ne suis pas vraiment un expert, mais j'en sais assez pour savoir que ce n'est pas une représentation UTF8, c'est UTF16, ou peut-être UTF32, ou UCS16. Pour un point de code à 2 octets, ces trois peuvent être identiques.
Baxissimo
1

La recherche de tous les caractères non-ascii donne l'impression que l'on recherche des chaînes unicode ou a l'intention de supprimer lesdits caractères individuellement.

Pour les premiers, essayez l'un d'eux (la variable fileest utilisée pour l'automatisation):

 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

Le grep de vanille ne fonctionne pas correctement sans LC_ALL = C comme indiqué dans les réponses précédentes.

La plage ASCII est x00-x7F, l'espace est x20, puisque les chaînes ont des espaces, la plage négative l'omet.

La plage non ASCII est x80-xFF, puisque les chaînes ont des espaces, la plage positive l'ajoute.

La chaîne est présumée être au moins 7 caractères consécutifs dans la plage. {7,}.

Pour une sortie lisible par le shell, uchardet $filerenvoie une estimation du codage du fichier qui est passé à iconv pour une interpolation automatique.

noabody
la source
Ceci est très utile en raison de la mention de la uchardetcommande. Merci pour cet avertissement!
bballdave025