Двоичные файлы grepping и UTF16
Стандарт grep
/pcregrep
и т.д. удобно использовать с двоичными файлами для данных ASCII или UTF8 - есть ли простой способ заставить их попробовать UTF16 тоже (желательно одновременно, но вместо этого будет делать)?
Данные, которые я пытаюсь получить, это все ASCII в любом случае (ссылки в библиотеках и т.д.), он просто не найден, как иногда там 00 между любыми двумя символами, а иногда и нет.
Я не вижу никакого способа сделать это семантически, но эти 00 должны делать трюк, но я не могу легко использовать их в командной строке.
Ответы
Ответ 1
Самый простой способ - просто преобразовать текстовый файл в 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.
EDIT: Здесь действительно действительно сумасшедший, который работает, но не дает вам очень полезной информации:
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 так, чтобы запрос и вход совпадали.
К сожалению, я думаю, что это приведет к распечатке файла ENTIRE, если есть одно совпадение. Кроме того, это не будет работать, если utf-16 в вашем двоичном файле будет храниться в другой форме, чем ваша машина.
EDIT2: Получил это!!!!
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
Ответ 2
Вы можете явно указать нули (00s) в строке поиска, хотя вы получите результаты с нулями, поэтому вы можете перенаправить вывод в файл, чтобы вы могли просмотреть его с помощью разумного редактора или передать его через sed, чтобы заменить нули. Поиск "bar" в *.utf16.txt:
grep -Pa "b\x00a\x00r" *.utf16.txt | sed 's/\x00//g'
"-P" сообщает grep принять синтаксис regexp Perl, который позволяет \x00 расширяться до нуля, а -a говорит ему игнорировать тот факт, что Unicode выглядит как двоичный.
Ответ 3
Я обнаружил, что приведенное ниже решение лучше всего работает для меня, с 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
Я использую это все время после сброса реестра 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"
Ответ 5
Мне нужно было сделать это рекурсивно, и вот что я придумал:
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 цикл; для каждой строки списка путей файла, поместите путь в $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
: escaping намного проще. Если я использовал выражение sed, мне приходится беспокоиться о наличии символов регулярных выражений в именах файлов, что в моем случае было много. nl
намного глубже, чем sed
, и просто возьмет параметр -s
в буквальном смысле, а оболочка обработает экранирование для меня.)
Итак, к концу этого конвейера я конвертировал кучу файлов в строки utf-8 с префиксом имени файла, который я тогда grep. Если есть совпадения, я могу указать, в каком файле они находятся из префикса.
Предостережения
- Это намного медленнее, чем
grep -R
, потому что я создаю новую копию iconv
, nl
, cut
и grep
для каждого отдельного файла. Это ужасно.
- Все, что не является входом utf-16le, выйдет как полный мусор, поэтому если есть обычный файл ASCII, содержащий "somestring", эта команда не сообщит об этом - вам нужно сделать обычный
grep -R
а также эту команду (и если у вас несколько типов кодировки Unicode, например, некоторые файлы большого и младшего порядка, вам нужно настроить эту команду и запустить ее снова для каждой различной кодировки).
- Файлы, имя которых содержит "somestring", будут отображаться на выходе, даже если их содержимое не имеет совпадений.
Ответ 6
ripgrep
Используйте утилиту ripgrep
для grep файлов UTF-16.
ripgrep поддерживает поиск файлов в текстовых кодировках, отличных от UTF-8, таких как UTF-16, latin-1, GBK, EUC-JP, Shift_JIS и других. (-E
некоторая поддержка автоматического определения UTF-16. Другие кодировки текста должны быть специально указаны с помощью -E
/--Encoding flag.
)
Пример синтаксиса:
rg sometext file
Чтобы сбросить все строки, выполните: rg -N. file
rg -N. file
Ответ 7
Утверждение sed больше, чем я могу обернуть вокруг себя. У меня есть упрощенный, далеко не идеальный TCL script, который, я думаю, выполняет работу OK с моей тестовой точкой:
#!/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"
}
}
Ответ 8
Я добавил это как комментарий к принятому ответу выше, но чтобы его было легче читать. Это позволяет вам искать текст в кучке файлов, а также отображать имена файлов, которые он находит в тексте. Во всех этих файлах есть расширение .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
Ответ 9
Вы можете использовать следующие 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
Источник: Как использовать Ruby's readlines.grep для файлов UTF-16?
Ответ 10
ugrep (Universal grep) поддерживает Unicode, файлы UTF-8/16/32, обнаруживает недопустимый Unicode для обеспечения правильных результатов, отображает текстовые и двоичные файлы, а также работает быстро и бесплатно:
ugrep ищет входные UTF-8/16/32 и другие форматы. Опция --encoding позволяет выполнять поиск во многих других форматах файлов, таких как ISO-8859-1, EBCDIC и кодовых страницах 437, 850, 858, 1250–1258.
Скачать угреп с GitHub