web-dev-qa-db-fra.com

Comment puis-je grep pour tous les caractères non-ASCII?

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

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

Mais cela retourne chaque ligne du fichier, que la ligne contienne ou non un caractère dans la plage spécifiée.

Est-ce que la syntaxe est incorrecte ou est-ce que je fais autre chose? J'ai aussi essayé:

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

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

320
pconrey

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 effectuer un grep inverse.

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

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

ceci est hautement expérimental et grep -P peut avertir qu’il n’a pas été implémenté fonctionnalités.

429
jerrymouse

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

Ainsi, la première solution, par exemple, deviendrait:

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

(qui utilise essentiellement les caractères externes à la plage hexadécimale ASCII: de\x00 à\x7F)

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

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

Y at-il des avantages ou des inconvénients que quiconque peut penser?

105
pvandenberk

Ce qui suit fonctionne pour moi:

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

Les caractères non-ASCII commencent à 0x80 et passent à 0xFF lors de la recherche d'octets. Grep (et sa famille) ne font pas de traitement Unicode pour fusionner des caractères multi-octets dans une seule entité afin de faire correspondre les expressions rationnelles que vous semblez vouloir. L'option -P dans mon grep permet d'utiliser des échappements \xdd dans les classes de caractères pour accomplir ce que vous voulez.

66
Thelema

En Perl

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

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.

Définir LC_COLLATE=C évite les mauvaises surprises quant à la signification des plages de caractères dans de nombreux endroits. La définition de LC_CTYPE=C est nécessaire pour faire correspondre les caractères à un octet - sinon, la commande omettrait des séquences d'octets non valides dans le codage actuel. Définir LC_ALL=C évite les effets dépendants de la localisation.

35
Gilles

Voici une autre variante que j'ai trouvée qui produit complètement des résultats différents de la recherche de grep pour [\x80-\xFF] dans la réponse acceptée. Peut-être sera-t-il utile de trouver d'autres personnages non-ascii:

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

Remarque: le grep (un Mac) de mon ordinateur ne disposant pas de l'option -P, j'ai donc utilisé brew install grep et démarré l'appel ci-dessus avec ggrep au lieu de grep.

21
ryanm

Le code suivant fonctionne:

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

Remplacez /tmp par le nom du répertoire dans lequel vous souhaitez effectuer la recherche.

8
user7417071

Recherche de caractères non imprimables.

Je suis d'accord avec Harvey ci-dessus, dans les commentaires, il est souvent plus utile de rechercher des caractères non imprimables OR, 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" 

Il est également utile d’ajouter -c (nombre de motifs trouvés) à grep lors de la recherche de caractères non imprimables, car les chaînes correspondantes peuvent gâcher le terminal.

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

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

panne:

\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

Par exemple. exemple pratique d'utilisation find pour grep tous les fichiers du répertoire courant:

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

Vous voudrez peut-être ajuster le grep parfois. par exemple. 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
3
gaoithe

Étrangement, je devais le faire aujourd'hui! J'ai fini par utiliser Perl car 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 \u2212 dans 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 }' '{}' \;
1
dty

La recherche de tous les caractères non ASCII donne l’impression que l’on cherche des chaînes Unicode ou a l’intention de les effacer individuellement.

Dans le premier cas, essayez l’une de ces méthodes (la variable file est 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

Vanilla grep 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 supposée contenir au moins 7 caractères consécutifs dans la plage. {7,}.

uchardet $file renvoie une estimation du codage de fichier qui est passé à iconv pour une interpolation automatique.

0
noabody

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

grep -v $'\u200d'
0
arezae