2010-09-20 9 views
45

Standard grep/pcregrep etc peut facilement être utilisé avec des fichiers binaires pour les données ASCII ou UTF8 - existe-t-il un moyen simple de les faire essayer UTF16 aussi (de préférence simultanément, mais plutôt faire)? Les données que j'essaye d'obtenir sont tout ASCII de toute façon (références dans les bibliothèques etc.), elles ne sont pas trouvées car parfois il y a 00 entre deux caractères, et parfois il n'y en a pas.greding fichiers binaires et UTF16

je ne vois aucun moyen pour le faire sémantiquement, mais ces 00s devrait faire l'affaire, sauf que je ne peux pas les utiliser facilement sur la ligne de commande.

+0

... ce n'est pas ASCII si les caractères ont une longueur de deux octets. –

+0

Je veux dire la plage de caractères ASCII (U + 0000 à U + 007F), pas le codage ASCII. – taw

Répondre

51

La façon la plus simple est de convertir tout le fichier texte au format UTF-8 et tuyau à grep:

iconv -f utf-16 -t utf-8 file.txt | grep query 

J'ai essayé de faire le contraire (convertir ma requête en utf-16), mais il semble que si grep n'aime pas ça. Je pense que cela pourrait avoir à voir avec l'endianness, mais je ne suis pas sûr.

Il semble que grep convertir une requête qui est utf-16 à utf-8/ascii. Voici ce que j'ai essayé:

grep `echo -n query | iconv -f utf-8 -t utf-16 | sed 's/..//'` test.txt 

Si test.txt est un fichier utf-16 cela ne fonctionnera pas, mais il ne fonctionne si test.txt est ascii. Je peux seulement conclure que grep convertit ma requête en ascii.

EDIT: Voici vraiment un vraiment fou ce genre d'œuvres, mais ne vous donne pas d'information très utile:

hexdump -e '/1 "%02x"' test.txt | grep -P `echo -n Test | iconv -f utf-8 -t utf-16 | sed 's/..//' | hexdump -e '/1 "%02x"'` 

Comment ça marche? Eh bien, il convertit votre fichier en hexadécimal (sans mise en forme supplémentaire que hexdump applique généralement). Ça canalise ça dans grep. Grep utilise une requête qui est construite en faisant écho à votre requête (sans retour à la ligne) dans iconv qui la convertit en utf-16. Ceci est ensuite redirigé vers sed pour supprimer la nomenclature (les deux premiers octets d'un fichier utf-16 utilisé pour déterminer l'endianness). Ceci est ensuite redirigé vers hexdump afin que la requête et l'entrée soient les mêmes.

Malheureusement, je pense que cela va finir par imprimer le fichier entier s'il y a une seule partie. Cela ne fonctionnera pas non plus si l'utf-16 dans votre fichier binaire est stocké dans une autre endianness que votre machine.

EDIT2: Got it !!!!

grep -P `echo -n "Test" | iconv -f utf-8 -t utf-16 | sed 's/..//' | hexdump -e '/1 "x%02x"' | sed 's/x/\\\\x/g'` test.txt 

Ceci recherche la version hexagonale de la chaîne Test (en utf-16) dans le fichier test.txt

+1

'iconv' ne fonctionnera pas, car c'est un fichier binaire beaucoup de données non-utf-16, et' iconv' se termine lors de la première erreur. – taw

+0

Ouch ... Je cherche toujours à donner à grep une requête utf-16 par curiosité (je ne pense pas qu'elle convertisse parce qu'elle ne connaît pas vraiment l'encodage, ça doit être quelque chose d'étrange) et je '' Je vais vous dire si je viens avec quelque chose. –

+0

Découvrez ma modification. Quelque chose qui fonctionne –

0

La déclaration sed est plus que je peux envelopper ma tête. J'ai un simpliste, loin d'être parfait script Tcl que je pense fait un travail OK avec mon point de test d'un:

#!/usr/bin/tclsh 

set insearch [lindex $argv 0] 

set search "" 

for {set i 0} {$i<[string length $insearch]-1} {incr i} { 
    set search "${search}[string range $insearch $i $i]." 
} 
set search "${search}[string range $insearch $i $i]" 

for {set i 1} {$i<$argc} {incr i} { 
    set file [lindex $argv $i] 
    set status 0 
    if {! [catch {exec grep -a $search $file} results options]} { 
     puts "$file: $results" 
    } 
} 
4

J'utilise celui-ci tout le temps après le dumping du registre Windows en tant que sa sortie est unicode . Cela fonctionne sous Cygwin.

$ regedit /e registry.data.out 
$ file registry.data.out 
registry.data.out: Little-endian **UTF-16 Unicode text**, with CRLF line terminators 

$ sed 's/\x00//g' registry.data.out | egrep "192\.168" 
"Port"="192.168.1.5" 
"IPSubnetAddress"="192.168.189.0" 
"IPSubnetAddress"="192.168.102.0" 
[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Print\Monitors\Standard TCP/IP Port\Ports\192.168.1.5] 
"HostName"="192.168.1.5" 
"Port"="192.168.1.5" 
"LocationInformation"="http://192.168.1.28:1215/" 
"LocationInformation"="http://192.168.1.5:80/WebServices/Device" 
"LocationInformation"="http://192.168.1.5:80/WebServices/Device" 
"StandaloneDhcpAddress"="192.168.173.1" 
"ScopeAddressBackup"="192.168.137.1" 
"ScopeAddress"="192.168.137.1" 
"DhcpIPAddress"="192.168.1.24" 
"DhcpServer"="192.168.1.1" 
"0.0.0.0,0.0.0.0,192.168.1.1,-1"="" 
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports\192.168.1.5] 
"HostName"="192.168.1.5" 
"Port"="192.168.1.5" 
"LocationInformation"="http://192.168.1.28:1215/" 
"LocationInformation"="http://192.168.1.5:80/WebServices/Device" 
"LocationInformation"="http://192.168.1.5:80/WebServices/Device" 
"StandaloneDhcpAddress"="192.168.173.1" 
"ScopeAddressBackup"="192.168.137.1" 
"ScopeAddress"="192.168.137.1" 
"DhcpIPAddress"="192.168.1.24" 
"DhcpServer"="192.168.1.1" 
"0.0.0.0,0.0.0.0,192.168.1.1,-1"="" 
"MRU0"="192.168.16.93" 
[HKEY_USERS\S-1-5-21-2054485685-3446499333-1556621121-1001\Software\Microsoft\Terminal Server Client\Servers\192.168.16.93] 
"A"="192.168.1.23" 
"B"="192.168.1.28" 
"C"="192.168.1.200:5800" 
"192.168.254.190::5901/extra"=hex:02,00 
"00"="192.168.254.190:5901" 
"ImagePrinterPort"="192.168.1.5" 
+0

Je suppose que cette méthode a peu de chances d'être faussement positive, mais c'est probablement ce qui est recherché dans 99,9% des cas. Cela fonctionne aussi pour moi sous MINGW64 Git Bash. – mwfearnley

0

J'ai ajouté cela comme commentaire à la réponse acceptée ci-dessus mais pour la rendre plus lisible. Cela vous permet de rechercher du texte dans un tas de fichiers tout en affichant les noms de fichiers pour lesquels il trouve le texte. Tous ces fichiers ont une extension .reg car je recherche dans les fichiers de registre Windows exportés. Remplacez simplement .reg par n'importe quelle extension de fichier.

// Define grepreg in bash by pasting at bash command prompt 
grepreg() 
{ 
    find -name '*.reg' -exec echo {} \; -exec iconv -f utf-16 -t utf-8 {} \; | grep "$1\|\.reg" 
} 

// Sample usage 
grepreg SampleTextToSearch 
8

Vous pouvez inclure explicitement les valeurs nulles (00s) dans la chaîne de recherche, bien que vous obtiendrez des résultats avec des valeurs nulles, de sorte que vous pouvez rediriger la sortie vers un fichier afin que vous puissiez regarder avec un éditeur raisonnable ou passez-le à travers sed pour remplacer les nulls. Pour rechercher "bar" dans * .utf16.txt:

grep -Pa "b\x00a\x00r" *.utf16.txt | sed 's/\x00//g' 

Le "-P" indique grep d'accepter la syntaxe Perl regexp, qui permet \ x00 étendre à null, et -a dit à ignorer le fait que Unicode lui ressemble binaire.

+0

Bonne technique, je n'y ai pas pensé. Le drapeau '-a' pour grep est la magie ici. En supposant que vous n'ayez pas de gros fichiers à rechercher (auquel cas cela pourrait être trop lent), vous pouvez le rendre un peu plus facile à taper en spécifiant '.' au lieu de' \ x00'. Le '.' va correspondre à tout, pas seulement un null. Ce n'est peut-être pas toujours ce que vous voulez mais probablement la plupart du temps ira bien. Souvent, le sed pour effacer les zéros n'est pas nécessaire non plus - ils n'impriment rien en sortie. Donc, pour votre exemple, juste 'grep -a b.a.r * .utf16.txt' devrait fonctionner. –

4

que je devais faire ce récursive, et voici ce que je suis venu avec:

find -type f | while read l; do iconv -s -f utf-16le -t utf-8 "$l" | nl -s "$l: " | cut -c7- | grep 'somestring'; done 

Ceci est absolument horrible et très lent; Je suis certain qu'il ya une meilleure façon et j'espère que quelqu'un peut l'améliorer - mais j'étais pressé: P

Ce que les pièces font:

find -type f 

donne une liste récursive des noms de fichiers avec des chemins par rapport au courant

while read l; do ... done 

Boucle de boucle; pour chaque ligne de la liste des chemins de fichier, placez le chemin dans $l et faites le chose dans la boucle. (Pourquoi ai-je utilisé une boucle shell à la place de xargs, ce qui aurait été beaucoup plus rapide: j'ai besoin de préfixer chaque ligne de la sortie avec le nom du fichier en cours ... Impossible de penser à un moyen de le faire si je nourrissais plusieurs fichiers à la fois à iconv, et que je vais être faire un fichier à la fois de toute façon, la boucle shell est plus facile syntaxe/échappement)

iconv -s -f utf-16le -t utf-8 "$l" 

Convertir le fichier nommé dans $l:. suppose que le fichier d'entrée est utf-16 petit-boutiste et le convertit en utf-8. Le -s fait iconv se taire sur toutes les erreurs de conversion (il y aura beaucoup, car certains fichiers dans cette structure de répertoires ne sont pas utf-16). La sortie de cette conversion va à stdout.

nl -s "$l: " | cut -c7- 

C'est un hack: nl inserts de numéros de ligne, mais il arrive d'avoir un « utiliser cette chaîne arbitraire pour séparer le numéro de la ligne » paramètre, donc je mis le nom du fichier (suivi par deux points et de l'espace) dans cela. Ensuite, j'utilise cut pour supprimer le numéro de ligne, en ne laissant que le préfixe du nom de fichier. (Pourquoi je n'ai pas utilisé sed: s'échapper est beaucoup plus facile de cette façon.Si j'ai utilisé une expression sed, je dois m'inquiéter des caractères d'expression régulière dans les noms de fichiers, qui dans mon cas il y avait beaucoup de nl est beaucoup plus bête et prendra simplement le paramètre -s entièrement et le shell gère l'échappement pour moi.)

Donc, à la fin de ce pipeline, j'ai converti un tas de fichiers en lignes d'utf-8 , préfixé avec le nom de fichier, que je grep ensuite. S'il y a des correspondances, je peux dire dans quel fichier elles se trouvent depuis le préfixe.

Avertissements

  • Cela est beaucoup, beaucoup plus lent que grep -R, parce que je fraye une nouvelle copie de iconv, nl, cut et grep pour chaque fichier. C'est horrible.Tout ce qui n'est pas utf-16le entrera comme une poubelle complète, donc s'il y a un fichier ASCII normal qui contient 'somestring', cette commande ne le signalera pas - vous devez aussi faire un grep -R normal comme cette commande (et si vous avez plusieurs types de codage Unicode, comme certains fichiers big-endian et certains little-endian, vous devez ajuster cette commande et l'exécuter à nouveau pour chaque encodage différent).
  • Les fichiers dont le nom contient 'somestring' apparaîtront dans la sortie, même si leur contenu ne correspond pas.
+0

Totalement dégoûtant. Et veeeery utile. thx –

+0

Je devais faire 'trouver. -type f' sur OS X –

0

J'ai trouvé la solution ci-dessous fonctionne le mieux pour moi, de https://www.splitbits.com/2015/11/11/tip-grep-and-unicode/

Grep ne joue pas bien avec Unicode, mais il peut être contourné. Par exemple, pour trouver,

Some Search Term 

dans un fichier UTF-16, utilisez une expression régulière pour ignorer le premier octet de chaque caractère,

S.o.m.e. .S.e.a.r.c.h. .T.e.r.m 

Aussi, dites-grep pour traiter le fichier sous forme de texte , en utilisant '-a', la commande finale ressemble à ceci,

grep -a 'S.o.m.e. .S.e.a.r.c.h. .T.e.r.m' utf-16-file.txt