unicode grep

unicode - grepping archivos binarios y UTF16



utf-16 table (7)

El estándar grep / pcregrep etc., se puede usar convenientemente con archivos binarios para datos ASCII o UTF8. ¿Hay alguna forma sencilla de hacer que prueben UTF16 también (de preferencia al mismo tiempo, pero en cambio lo harán)?

Los datos que trato 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, a veces, no.

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


Agregué esto como un comentario a la respuesta aceptada anteriormente, pero para que sea 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


Encontré que la siguiente solución funcionó mejor para mí, desde https://www.splitbits.com/2015/11/11/tip-grep-and-unicode/

Grep no funciona bien con Unicode, pero se puede solucionar. Por ejemplo, para encontrar,

Some Search Term

en un archivo UTF-16, use una expresión regular para ignorar el primer byte en cada carácter,

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

Además, dile a grep que trate 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


La declaración sed es más de lo que puedo entender. Tengo un script TCL simplista, lejos de perfecto, que creo que funciona 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" } }


La forma más fácil es simplemente convertir el archivo de texto a utf-8 y canalizarlo 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 que a 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 intenté:

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 funcionará, pero funciona si test.txt es ascii. Solo puedo concluir que grep está convirtiendo mi consulta en ascii.

EDITAR: Esta es realmente una locura que funciona pero no te da mucha información ú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"''`

¿Como 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: lo tengo !!!!

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 la versión hexadecimal de la cadena Test (en utf-16) en el archivo test.txt


Lo uso todo el tiempo después de eliminar el registro de Windows, ya que 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"


Necesitaba hacer esto recursivamente, y esto es lo que se me 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 mejor manera y espero que alguien pueda mejorar, pero tenía prisa: P

Qué hacen las piezas:

find -type f

da una lista recursiva de nombres de archivos con rutas relativas a la corriente

while read l; do ... done

Bash loop; para cada línea de la lista de rutas de archivos, coloque la ruta en $l y realice la operación en el bucle. (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 múltiples archivos a la vez a iconv, y como voy a estar haciendo un archivo a la vez de todos modos, el bucle de shell es más fácil de sintaxis / escape).

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

Convierta el archivo nombrado en $l : suponga que el archivo de entrada es utf-16 little-endian y conviértalo a utf-8. El -s hace que iconv se calle acerca de 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-

Esto es un hack: nl inserta números de línea, pero resulta que tiene un parámetro "use this arbitrary string para separar el número de la línea", así que puse el nombre de archivo (seguido 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 sed : escapar es mucho más fácil de esta manera. Si utilizo 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 que sed , y solo tomará los parámetros -s completamente literalmente, y el shell manejará el escape para mí.)

Entonces, al final de esta canalización, he convertido un grupo de archivos en líneas de utf-8, con el nombre de archivo prefijado, 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 contenga ''somestring'', este comando no lo informará; debe hacer una grep -R normal así 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 tienen coincidencias.

Puede incluir explícitamente los nulos (00s) en la cadena de búsqueda, aunque obtendrá resultados con valores nulos, por lo que es posible que desee redirigir la salida a un archivo para que pueda verlo con un editor razonable o canalizarlo 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" le dice a grep que acepte la sintaxis de regexp de Perl, que permite que / x00 se expanda a nulo, y -a le dice que ignore el hecho de que Unicode tiene el mismo aspecto binario.