web-dev-qa-db-fra.com

grepping des fichiers binaires et UTF16

Les variables standard grep/pcregrep etc. peuvent être utilisées avec des fichiers binaires pour ASCII ou des données UTF8 - existe-t-il un moyen simple de les faire essayer aussi UTF16 (de préférence simultanément, mais fera l'affaire)?

Les données que j'essaie d'obtenir sont tout ASCII de toute façon (références dans des bibliothèques, etc.), elles ne sont tout simplement pas trouvées car il y a parfois 00 entre deux caractères, et parfois il n'y en a pas.

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

53
taw

Le moyen le plus simple est de convertir le fichier texte en utf-8 et de le diriger vers 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 grep n'aime pas cela. Je pense que cela pourrait avoir un rapport avec l’endianisme, mais je ne suis pas sûr.

Il semble que grep va convertir une requête utf-16 en 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 cela fonctionnera si test.txt est ascii. Je ne peux que conclure que grep convertit ma requête en ascii.

EDIT: Voici un très fou qui fonctionne mais ne vous donne pas beaucoup d’informations utiles:

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 au format hexadécimal (sans aucun formatage supplémentaire que hexdump applique habituellement). Il achemine cela dans grep. Grep utilise une requête qui est construite en faisant écho à votre requête (sans nouvelle ligne) dans iconv qui la convertit en utf-16. Cela est ensuite dirigé vers sed pour supprimer la nomenclature (les deux premiers octets d'un fichier utf-16 utilisé pour déterminer l'endianité). Ceci est ensuite redirigé vers hexdump afin que la requête et l'entrée soient identiques.

Malheureusement, je pense que cela finira par imprimer le fichier ENTIRE s'il existe une seule correspondance. De plus, cela ne fonctionnera pas si l’utf-16 de votre fichier binaire est stocké dans une finalité différente de celle de votre machine.

EDIT2: Je l'ai !!!

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 hexadécimale de la chaîne Test (dans utf-16) dans le fichier test.txt

63
Niki Yoshiuchi

Vous pouvez explicitement inclure les valeurs NULL (00) dans la chaîne de recherche, mais vous obtiendrez des résultats avec des valeurs NULL. Vous pouvez ainsi rediriger la sortie vers un fichier afin de pouvoir l'examiner avec un éditeur raisonnable ou la diriger vers sed. remplace les nuls. 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, ce qui permet à\x00 de se développer jusqu'à null, et le -a lui dit d'ignorer le fait qu'Unicode lui paraît binaire.

12
Ethan Bradford

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

Grep ne fonctionne pas bien avec Unicode, mais il peut être corrigé. 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 

De plus, dites à grep de traiter le fichier en tant que 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
5
nirmal

Je devais le faire récursivement, 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

C'est absolument horrible et très lent; Je suis certain qu'il existe un meilleur moyen et j'espère que quelqu'un pourra l'améliorer - mais j'étais pressé: P

Que font les pièces:

find -type f

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

while read l; do ... done

Boucle Bash; pour chaque ligne de la liste des chemins de fichiers, placez-le dans $l et effectuez la chose dans la boucle. (Pourquoi j'ai utilisé une boucle Shell au lieu de xargs, ce qui aurait été beaucoup plus rapide: je dois préfixer chaque ligne de la sortie avec le nom du fichier actuel. Je ne pouvais pas penser à un moyen de le faire si je nourrissais plusieurs fichiers à la fois pour iconv, et comme je vais tout de même créer un fichier à la fois, la boucle Shell est plus simple en syntaxe/échappement.)

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

Convertissez le fichier nommé dans $l: supposez que le fichier d'entrée est utf-16 little-endian et convertissez-le en utf-8. Le -s permet à iconv de se calmer à propos des erreurs de conversion (il y en aura beaucoup, car certains fichiers de cette structure de répertoires ne sont pas utf-16). Le résultat de cette conversion est envoyé à stdout.

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

Ceci est un hack: nl insère des numéros de ligne, mais le paramètre "utilisez cette chaîne arbitraire pour séparer le nombre de la ligne", je mets donc le nom de fichier (suivi de deux points et d'espace). 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: échapper est beaucoup plus facile de cette façon. Si j'utilisais une expression sed, je devrais m'inquiéter des caractères d'expression régulière dans les noms de fichiers, qui dans mon cas étaient nombreux. nl est beaucoup plus bête que sed, et prend simplement le paramètre -s de manière littérale, 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ées avec le nom de fichier, que j'ai ensuite grep. S'il y a des correspondances, je peux savoir quel fichier ils contiennent à partir du préfixe.

Mises en garde

  • C'est beaucoup, beaucoup plus lent que grep -R, car je crée une nouvelle copie de iconv, nl, cut et grep pour chaque fichier. C'est horrible.
  • Tout ce qui n’est pas une entrée utf-16le sera considéré comme un déchet complet. Ainsi, s’il existe un fichier ASCII normal contenant «somestring», cette commande ne le signalera pas - vous devez créer un grep -R normal comme ainsi que cette commande (et si vous avez plusieurs types de codage Unicode, comme certains fichiers big-endian et little-endian, vous devez ajuster cette commande et la réexécuter pour chaque codage différent).
  • Les fichiers dont le nom contient «somestring» apparaîtront dans la sortie, même si leur contenu ne correspond à rien.
4
Felix

J'utilise celui-ci tout le temps après avoir vidé le registre Windows car sa sortie est unicode. Ceci 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"
4
Mike Cush

J'ai ajouté ceci en tant que commentaire à la réponse acceptée ci-dessus, mais pour faciliter la lecture. Cela vous permet de rechercher du texte dans un groupe de fichiers tout en affichant les noms de fichiers recherchant le texte. Tous ces fichiers ont une extension .reg puisque je cherche dans les fichiers de registre Windows exportés. Il suffit de remplacer .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
0
Andrew Stern

ripgrep

Utilisez ripgrep utility pour grep les fichiers UTF-16.

ripgrep prend en charge la recherche de fichiers dans des codages de texte autres que UTF-8, tels que UTF-16, latin-1, GBK, EUC-JP, Shift_JIS, etc. (Une certaine prise en charge de la détection automatique du format UTF-16 est fournie. Les autres encodages de texte doivent être spécifiés avec le code -E/--encoding flag.)

Exemple de syntaxe:

rg sometext file

Pour vider toutes les lignes, exécutez: rg -N . file.

0
kenorb

Vous pouvez utiliser l'un des doublures suivantes:

Ruby -e "puts File.open('file.txt', mode:'rb:BOM|UTF-16LE').readlines.grep(Regexp.new 'PATTERN'.encode(Encoding::UTF_16LE))"

Pour simplifier, ceci peut être défini comme la fonction Shell comme:

grep-utf16() { Ruby -e "puts File.open('$2', mode:'rb:BOM|UTF-16LE').readlines.grep(Regexp.new '$1'.encode(Encoding::UTF_16LE))"; }

Ensuite, il sera utilisé de la même manière que grep:

grep-utf16 PATTERN file.txt

Source: Comment utiliser readlines.grep de Ruby pour les fichiers UTF-16?

0
kenorb

La déclaration de Sed est plus que ce que je peux comprendre. J'ai un script TCL simpliste et loin d'être parfait qui, à mon avis, fonctionne bien avec mon point de test:

#!/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"
    }
}
0
user1117791