2010-09-20 40 views
45

estándar grep/pcregrep etc. se puede usar convenientemente con archivos binarios para datos ASCII o UTF8 - ¿hay una manera simple de hacer que prueben también UTF16 (preferiblemente al mismo tiempo, pero en su lugar)?grepping archivos binarios y UTF16

Los datos que estoy tratando de obtener son todos ASCII de todos modos (referencias en bibliotecas, etc.), simplemente no se encuentran ya que a veces hay 00 entre dos caracteres, y algunas veces no.

No veo ninguna forma de hacerlo semánticamente, pero estos 00 deberían funcionar, excepto que no puedo usarlos fácilmente en la línea de comandos.

+0

... no es ASCII si los caracteres tienen dos bytes de longitud. –

+0

Me refiero al rango de caracteres ASCII (U + 0000 a U + 007F), no codificación ASCII. – taw

Respuesta

51

La forma más fácil es simplemente convertir el archivo de texto a UTF-8 y el tubo que a grep:

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

Traté de hacer lo contrario (convertir mi consulta a UTF-16), pero parece como aunque grep no le gusta eso. Creo que podría tener que ver con endianness, pero no estoy seguro.

Parece que grep convertirá una consulta que es utf-16 a utf-8/ascii. Esto es lo que he intentado:

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

Si test.txt es un archivo UTF-16, esto no funciona, pero funciona si test.txt es ASCII. Solo puedo concluir que grep está convirtiendo mi consulta en ascii.

EDIT: Aquí hay una muy muy loco ese tipo de obras, pero no le da información muy útil:

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

¿Cómo funciona? Bueno, convierte su archivo en hexadecimal (sin ningún formato adicional que se aplique hexdump). Lo canaliza en grep. Grep está utilizando una consulta que se construye haciendo eco de su consulta (sin una nueva línea) en iconv, que la convierte a utf-16. Esto se canaliza a sed para eliminar la BOM (los primeros dos bytes de un archivo utf-16 utilizado para determinar la endianidad). Esto se canaliza en hexdump para que la consulta y la entrada sean las mismas.

Desafortunadamente, creo que esto terminará imprimiendo el archivo COMPLETO si hay una sola coincidencia. Además, esto no funcionará si el utf-16 en su archivo binario se almacena en una endianidad diferente a la de su máquina.

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 

Esto busca para la versión hexagonal de la cadena Test (en UTF-16) en el archivo test.txt

+1

'iconv' no funcionará, ya que es un archivo binario una gran cantidad de datos no-utf-16, y' iconv' sale en el primer error. – taw

+0

Ouch ... Todavía estoy buscando dar una consulta grep a utf-16 por curiosidad (no creo que se convierta porque realmente no sabe la codificación, debe estar haciendo algo extraño) y yo ' Te dejaré saber si se me ocurre algo. –

+0

Mira mi edición. Tengo algo que funciona. –

0

La declaración sed es más de lo que puedo envolver mi cabeza alrededor. Tengo un simplista, que dista de ser perfecto guión TCL que creo que hace un trabajo bien con mi punto de prueba de uno:

#!/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

utilizo éste todo el tiempo después de volcar el registro de Windows como su salida es Unicode . Esto se ejecuta bajo 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

Supongo que de esta manera hay pocas probabilidades de falsos positivos, pero es probablemente lo que se quiere el 99.9% del tiempo. También funciona para mí bajo MINGW64 Git Bash. – mwfearnley

0

Lo agregué como comentario a la respuesta aceptada anteriormente, pero para hacerlo más fácil de leer. Esto le permite buscar texto en un grupo de archivos al mismo tiempo que muestra los nombres de archivo que encuentra el texto. Todos estos archivos tienen una extensión .reg ya que estoy buscando archivos exportados de Registro de Windows. Simplemente reemplace .reg con cualquier extensión de archivo.

// 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

Puede incluir explícitamente los nulos (00s) en la cadena de búsqueda, aunque obtendrá resultados con nulos, por lo que puede volver a dirigir la salida a un archivo para que pueda mirarlo con un editor razonable , o pídalo a través de sed para reemplazar los nulos. Para buscar "barra" en .utf16.txt *:

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

La "-P" indica a grep para aceptar la sintaxis de expresiones regulares Perl, que permite x00 \ expandirse a nula, y la -a dice que ignorar el hecho de que Unicode parece binario.

+0

Buena técnica, no pensé en esto. La bandera '-a' para grep es la magia aquí. suponiendo que no tiene grandes archivos para buscar (en cuyo caso esto podría ser demasiado lento), puede hacer que sea un poco más fácil de escribir simplemente especificando '.' en lugar de' \ x00'. El '.' coincidirá con cualquier cosa, no solo con un valor nulo. Eso no siempre es lo que quieres, pero probablemente la mayor parte del tiempo estará bien. A menudo, el sed para eliminar los nulos tampoco es necesario; no imprimen nada en la salida. Entonces, para su ejemplo, solo 'grep -a b.a.r * .utf16.txt' debería funcionar. –

4

que tenía que hacer esto de forma recursiva, y esto es lo que ocurrió:

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

Esto es absolutamente horrible y muy lento; Estoy seguro de que hay una manera mejor y espero que alguien pueda mejorar en él - pero yo estaba en un apuro: P

Lo que hacen las piezas:

find -type f 

da una lista recursiva de nombres de archivo con las rutas relativo a la actual

while read l; do ... done 

Bash loop; para cada línea de la lista de rutas de archivos, ingrese la ruta en $l y haga lo mismo en el ciclo. (Por qué utilicé un bucle de shell en lugar de xargs, lo que hubiera sido mucho más rápido: tengo que prefijar cada línea de la salida con el nombre del archivo actual. No podía pensar en una forma de hacerlo si estaba alimentando varios archivos a la vez para iconv, y ya que voy a estar haciendo un archivo al mismo tiempo de todos modos, el lazo shell es la sintaxis más fácil/escape)

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

convertir el archivo nombrado en $l:. asumirá el archivo de entrada es utf-16 little-endian y lo convierte a utf-8. El -s hace que Iconv se calle con respecto a cualquier error de conversión (habrá mucho, porque algunos archivos en esta estructura de directorio no son utf-16). El resultado de esta conversión pasa a stdout.

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

Este es un truco: nl insertos números de línea, pero pasa a tener un parámetro "utilizar esta cadena arbitraria para separar el número de la línea", por lo que poner el nombre de archivo (seguida de dos puntos y espacio) en eso. Luego uso cut para quitar el número de línea, dejando solo el prefijo del nombre de archivo. (Por qué no usé sed: escapar es mucho más fácil de esta manera. Si utilicé una expresión sed, tengo que preocuparme de que haya caracteres de expresión regulares en los nombres de archivo, que en mi caso eran muchos. nl es mucho más tonto de sed, y que acaba de tomar el parámetro -s por completo, literalmente, y la cáscara se encarga del escape para mí.)

Así, a finales de este gasoducto, me he convertido en un montón de archivos en líneas de UTF-8 , prefijado con el nombre de archivo, que luego grep. Si hay coincidencias, puedo decir en qué archivo están del prefijo.

Advertencias

  • Esto es mucho, mucho más lento que grep -R, porque estoy generando una nueva copia de iconv, nl, cut y grep para cada archivo. Es horrible.
  • Todo lo que no sea entrada utf-16le saldrá como basura completa, por lo que si hay un archivo ASCII normal que contiene 'somestring', este comando no lo informará; también debe hacer una normal grep -R como este comando (y si tiene múltiples tipos de codificación Unicode, como algunos archivos big-endian y little little-endian, necesita ajustar este comando y ejecutarlo nuevamente para cada codificación diferente).
  • Los archivos cuyo nombre contenga 'somestring' aparecerán en la salida, incluso si sus contenidos no coinciden.
+0

Totalmente repugnante. Y veeeery útil. thx –

+0

Tenía que hacer 'encontrar. -type f' en OS X –

0

he encontrado la solución a continuación funcionó mejor para mí, de https://www.splitbits.com/2015/11/11/tip-grep-and-unicode/

Grep no juega bien con Unicode, pero se puede evitar. Por ejemplo, para encontrar,

Some Search Term 

en un archivo UTF-16, utilice una expresión regular para ignorar el primer byte de cada personaje,

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

También informe a grep para tratar el archivo como texto , usando '-a', el comando final se ve así,

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