grepping бинарные файлы и UTF16 - PullRequest
       18

grepping бинарные файлы и UTF16

57 голосов
/ 20 сентября 2010

Стандарт grep / pcregrep и т. Д. Удобно использовать с двоичными файлами для данных ASCII или UTF8 - есть ли простой способ заставить их попробовать UTF16 (желательно одновременно, но вместо этого)?

Данные, которые я пытаюсь получить, в любом случае все ASCII (ссылки в библиотеках и т. Д.), Они просто не обнаруживаются, так как иногда между любыми двумя символами есть 00, а иногда нет.

Я не вижу способа сделать это семантически, но эти 00 должны делать свое дело, за исключением того, что я не могу легко использовать их в командной строке.

Ответы [ 9 ]

64 голосов
/ 23 сентября 2010

Самый простой способ - просто преобразовать текстовый файл в utf-8 и передать его в grep:

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

Я пытался сделать обратное (преобразовать мой запрос в utf-16), но похоже, что grep это не нравится. Я думаю, что это может быть связано с порядком байтов, но я не уверен.

Кажется, что grep преобразует запрос utf-16 в utf-8 / ascii. Вот что я попробовал:

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

Если test.txt является файлом utf-16, это не сработает, но сработает, если test.txt имеет формат ascii. Я могу только заключить, что grep преобразует мой запрос в ascii.

РЕДАКТИРОВАТЬ: Вот действительно очень сумасшедший, который работает, но не дает вам много полезной информации:

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

Как это работает? Ну, он конвертирует ваш файл в шестнадцатеричный формат (без какого-либо дополнительного форматирования, который обычно применяется hexdump). Это передает это в grep. Grep использует запрос, который создается путем вывода вашего запроса (без новой строки) в iconv, который преобразует его в utf-16. Затем он передается в sed для удаления спецификации (первые два байта файла utf-16, используемые для определения порядка байтов). Затем он передается в hexdump, чтобы запрос и ввод совпадали.

К сожалению, я думаю, что это приведет к распечатке ВСЕГО файла, если будет одно совпадение. Также это не сработает, если utf-16 в вашем двоичном файле хранится в другом порядке, чем ваша машина.

РЕДАКТИРОВАТЬ 2: Понял !!!!

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

Поиск шестнадцатеричной версии строки Test (в utf-16) в файле test.txt

13 голосов
/ 10 ноября 2015

Вы можете явно включить нули (00) в строку поиска, хотя вы получите результаты с нулями, так что вы можете перенаправить вывод в файл, чтобы вы могли просмотреть его с помощью подходящего редактора или передать его по конвейеру.через sed, чтобы заменить нули.Чтобы найти «bar» в * .utf16.txt:

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

«-P» указывает grep принять синтаксис регулярного выражения Perl, который позволяет \ x00 расширяться до нуля, а -a сообщает емуигнорировать тот факт, что Unicode выглядит как двоичный код для него.

8 голосов
/ 02 марта 2018

Я обнаружил, что приведенное ниже решение работает лучше всего для меня, начиная с https://www.splitbits.com/2015/11/11/tip-grep-and-unicode/

Grep не очень хорошо работает с Unicode, но его можно обойти.Например, чтобы найти

Some Search Term

в файле UTF-16, используйте регулярное выражение для игнорирования первого байта в каждом символе,

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

Также скажите grep для обработкифайл как текст, используя '-a', последняя команда выглядит следующим образом:

grep -a 'S.o.m.e. .S.e.a.r.c.h. .T.e.r.m' utf-16-file.txt
4 голосов
/ 12 декабря 2015

Мне нужно было сделать это рекурсивно, и вот что я придумал:

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

Это абсолютно ужасно и очень медленно;Я уверен, что есть лучший способ, и я надеюсь, что кто-то может улучшить его - но я спешил: P

Что делают:

find -type f

дает рекурсивный списокимен файлов с путями относительно текущего

while read l; do ... done

Bash loop;для каждой строки списка путей к файлам поместите путь в $l и сделайте это в цикле.(Почему я использовал цикл оболочки вместо xargs, который был бы намного быстрее: мне нужно добавить в каждую строку вывода имя текущего файла. Не мог придумать, как это сделать, если бы я кормилнесколько файлов одновременно в iconv, и так как я собираюсь делать один файл за раз в любом случае, цикл оболочки упрощает синтаксис / экранирование.)

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

Преобразование файла с именем в $l:предположим, что входной файл имеет формат utf-16 little-endian и преобразует его в utf-8.-s заставляет iconv замолчать из-за любых ошибок преобразования (их будет много, потому что некоторые файлы в этой структуре каталогов не являются utf-16).Выходные данные этого преобразования отправляются на стандартный вывод.

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

Это хак: nl вставляет номера строк, но в нем есть параметр «использовать эту произвольную строку, чтобы отделить число от строки», поэтому я поместил имя файла (за которым следуют двоеточие и пробел).Затем я использую cut, чтобы убрать номер строки, оставив только префикс имени файла.(Почему я не использовал sed: экранирование гораздо проще. Если я использовал выражение sed, мне нужно беспокоиться о символах регулярных выражений в именах файлов, которых в моем случае было много. nl намного тупее, чем sed, и просто примет параметр -s буквально, а оболочка обрабатывает экранирование для меня.)

Итак, к концу этого конвейера я преобразовалкуча файлов в строки utf-8, с префиксом имени файла, которое я затем grep.Если есть совпадения, я могу определить, в каком файле они находятся, по префиксу.

Предостережения

  • Это намного, намного медленнее, чем grep -R, потому что я порождаюновая копия iconv, nl, cut и grep для каждого отдельного файла.Это ужасно.
  • Все, что не является вводом utf-16le, будет выглядеть как полный мусор, поэтому, если есть обычный ASCII-файл, содержащий 'somestring', эта команда не сообщит об этом - вам нужновыполните обычную команду grep -R, а также эту команду (и если у вас есть несколько типов кодировки Юникод, например, файлы с прямым порядком байтов и файлы с прямым порядком байтов, вам нужно настроить эту команду и запустить ее снова для каждой другой кодировки).
  • Файлы с именем somestring будут отображаться в выходных данных, даже если их содержимое не соответствует.
4 голосов
/ 30 августа 2014

Я использую это все время после сброса реестра Windows, поскольку его вывод - UnicodeЭто работает под 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"
2 голосов
/ 17 января 2019

ripgrep

Используйте ripgrep утилиту для grep файлов UTF-16.

ripgrep поддерживает поиск файлов в кодировках, отличных от UTF-8, таких как UTF-16, латинский-1, GBK, EUC-JP, Shift_JIS и другие.(Предоставляется некоторая поддержка автоматического определения UTF-16. Другие кодировки текста должны быть специально указаны с помощью -E / --encoding flag.)

Пример синтаксиса:

rg sometext file

Чтобы вывести все строки, выполните: rg -N . file.

0 голосов
/ 21 мая 2019

Вы можете использовать следующие однострочники Ruby:

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

Для простоты это можно определить как функцию оболочки, например:

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

используется аналогично grep:

grep-utf16 PATTERN file.txt

Источник: Как использовать readlines.grep Руби для файлов UTF-16?

0 голосов
/ 16 октября 2015

Я добавил это как комментарий к принятому ответу выше, но чтобы его было легче читать. Это позволяет вам искать текст в группе файлов, а также отображать имена файлов, по которым он находит текст. Все эти файлы имеют расширение .reg, так как я ищу в экспортированных файлах реестра Windows. Просто замените .reg на любое расширение файла.

// 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 голосов
/ 15 июля 2013

Заявление sed - это больше, чем я могу обернуть вокруг себя. У меня есть упрощенный, далеко не идеальный сценарий TCL, который, я думаю, хорошо работает с моей контрольной точкой:

#!/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"
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...