Бинарный grep на Linux? - PullRequest
       18

Бинарный grep на Linux?

28 голосов
/ 15 ноября 2010

Скажите, что я сгенерировал следующий двоичный файл:

# generate file:
python -c 'import sys;[sys.stdout.write(chr(i)) for i in (0,0,0,0,2,4,6,8,0,1,3,0,5,20)]' > mydata.bin

# get file size in bytes
stat -c '%s' mydata.bin

# 14

И, скажем, я хочу найти расположение всех нулей (0x00), используя синтаксис, подобный grep.

Лучшее, что я могу сделать на данный момент:

$ hexdump -v -e "1/1 \" %02x\n\"" mydata.bin | grep -n '00'

1: 00
2: 00
3: 00
4: 00
9: 00
12: 00

Однако это неявно преобразует каждый байт в исходном двоичном файле в многобайтовое представление ASCII, с которым работает grep; не совсем яркий пример оптимизации:)

Есть ли что-то вроде бинарного grep для Linux? Возможно также, что-то, что будет поддерживать синтаксис, подобный регулярному выражению, но также для байтовых «символов» - то есть я мог бы написать что-то вроде «a(\x00*)b» и сопоставить «ноль или более» вхождений байта 0 между байтами а '(97) и' б '(98)?

РЕДАКТИРОВАТЬ: Контекст заключается в том, что я работаю над драйвером, где я собираю 8-битные данные; что-то идет не так в данных, которые могут составлять от нескольких килобайт до мегабайт, и я хотел бы проверить наличие определенных подписей и места их появления. ( пока, я работаю с килобайтовыми фрагментами, поэтому оптимизация не так важна - но если я начинаю получать ошибки в мегабайтах длинных снимков, и мне нужно проанализировать их, я думаю, я бы хотел что-то более оптимизированное :) И особенно, я хотел бы кое-что, где я могу "grep" для байта как символа - hexdump заставляет меня искать строки на байт )

EDIT2: тот же вопрос, другой форум :) поиск в двоичном файле последовательности байтов

РЕДАКТИРОВАТЬ3: благодаря ответу @tchrist, вот также пример с 'grepping' и сопоставлением и отображением результатов (, хотя и не совсем такой же вопрос, как OP ):

$ perl -ln0777e 'print unpack("H*",$1), "\n", pos() while /(.....\0\0\0\xCC\0\0\0.....)/g' /path/to/myfile.bin

ca000000cb000000cc000000cd000000ce     # Matched data (hex)
66357                                  # Offset (dec)

Чтобы сопоставленные данные были сгруппированы в один байт (два шестнадцатеричных символа) каждый, необходимо указать «H2 H2 H2 ...», поскольку в сопоставляемой строке содержится столько байтов; так как мой матч '.....\0\0\0\xCC\0\0\0.....' занимает 17 байт, я могу написать '"H2"x17' на Perl. Каждый из этих «H2» будет возвращать отдельную переменную (как в списке), поэтому join также необходимо использовать для добавления пробелов между ними - в конечном итоге:

$ perl -ln0777e 'print join(" ", unpack("H2 "x17,$1)), "\n", pos() while /(.....\0\0\0\xCC\0\0\0.....)/g' /path/to/myfile.bin

ca 00 00 00 cb 00 00 00 cc 00 00 00 cd 00 00 00 ce
66357

Что ж, действительно, Perl - очень хорошая утилита для "бинарного поиска", я должен признать :) Пока вы правильно изучаете синтаксис:)

Ответы [ 6 ]

44 голосов
/ 18 июня 2013

Мне кажется, это работает:

grep --only-matching --byte-offset --binary --text --perl-regexp "<\x-hex pattern>" <file>

Краткая форма:

grep -obUaP "<\x-hex pattern>" <file>

Пример:

grep -obUaP "\x01\x02" /bin/grep

Вывод ( Cygwin двоичный):

153: <\x01\x02>
33210: <\x01\x02>
53453: <\x01\x02>

Таким образом, вы можете выполнить это снова, чтобы извлечь смещенияНо не забудьте снова использовать двоичный режим.

19 голосов
/ 15 ноября 2010

Кто-то, похоже, тоже был разочарован и написал свой собственный инструмент для этого (или, по крайней мере, что-то подобное):

14 голосов
/ 15 ноября 2010

однострочный ввод

Вот более короткая однострочная версия:

% perl -ln0e 'print tell' < inputfile

А вот чуть более длинная однострочная:

% perl -e '($/,$\) = ("\0","\n"); print tell while <STDIN>' < inputfile

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

% perl -MO=Deparse,-p -ln0e 'print tell'
BEGIN { $/ = "\000"; $\ = "\n"; }
LINE: while (defined(($_ = <ARGV>))) {
    chomp($_);
    print(tell);
}

Запрограммированный ввод

Если вы хотите поместить это в файл вместо вызова его из командной строкиВот несколько более явная версия:

#!/usr/bin/env perl

use English qw[ -no_match_vars ];

$RS  = "\0";    # input  separator for readline, chomp
$ORS = "\n";    # output separator for print

while (<STDIN>) {
    print tell();
}

А вот действительно длинная версия:

#!/usr/bin/env perl

use strict;
use autodie;  # for perl5.10 or better
use warnings qw[ FATAL all  ];

use IO::Handle;

IO::Handle->input_record_separator("\0");
IO::Handle->output_record_separator("\n");

binmode(STDIN);   # just in case

while (my $null_terminated = readline(STDIN)) {
    # this just *past* the null we just read:
    my $seek_offset = tell(STDIN);
    print STDOUT $seek_offset;  

}

close(STDIN);
close(STDOUT);

Однострочный вывод

Кстати, для создания тестового вводафайл, я не использовал твой большой длинный скрипт на Python;Я просто использовал этот простой однострочный Perl:

% perl -e 'print 0.0.0.0.2.4.6.8.0.1.3.0.5.20' > inputfile

Вы обнаружите, что Perl часто оказывается в 2-3 раза короче, чем Python, чтобы выполнять ту же работу.И вам не нужно идти на компромисс с ясностью;что может быть проще, чем одна строка выше?

Запрограммированный вывод

Я знаю, я знаю.Если вы еще не знаете язык, это может быть понятнее:

#!/usr/bin/env perl
@values = (
    0,  0,  0,  0,  2,
    4,  6,  8,  0,  1,
    3,  0,  5, 20,
);
print pack("C*", @values);

, хотя это тоже работает:

print chr for @values;

, как и

print map { chr } @values;

Хотя для тех, кто любит все строго, осторожно и все такое, это может быть больше того, что вы увидите:

#!/usr/bin/env perl

use strict;
use warnings qw[ FATAL all ];
use autodie;

binmode(STDOUT);

my @octet_list = (
    0,  0,  0,  0,  2,
    4,  6,  8,  0,  1,
    3,  0,  5, 20,
);

my $binary = pack("C*", @octet_list);
print STDOUT $binary;

close(STDOUT); 

TMTOWTDI

Perl поддерживает более одного способа сделать что-то, чтобы вымогу выбрать тот, который вам удобнее всего.Если бы это было что-то, что я планировал зарегистрировать как школьный или рабочий проект, я бы определенно выбрал более длинные, более осторожные версии - или, по крайней мере, добавил бы комментарий в сценарий оболочки, если бы использовал однострочники.

Вы можете найти документацию по Perl в своей системе.Просто введите

% man perl
% man perlrun
% man perlvar
% man perlfunc

и т. Д. В командной строке.Если вы хотите использовать в Интернете симпатичные версии, получите справочные страницы для perl , perlrun , perlvar и perlfunc из http://perldoc.perl.org.

10 голосов
/ 08 августа 2011

Программа bbe представляет собой sed -подобный редактор для двоичных файлов.См. документацию .

Пример с bbe :

bbe -b "/\x00\x00\xCC\x00\x00\x00/:17" -s -e "F d" -e "p h" -e "A \n" mydata.bin

11:x00 x00 xcc x00 x00 x00 xcd x00 x00 x00 xce

Пояснение

-b search pattern between //. each 2 byte begin with \x (hexa notation).
   -b works like this /pattern/:length (in byte) after matched pattern
-s similar to 'grep -o' suppress unmatched output 
-e similar to 'sed -e' give commands
-e 'F d' display offsets before each result here: '11:'
-e 'p h' print results in hexadecimal notation
-e 'A \n' append end-of-line to each result

Вы также можете передать его на sed для более чистого вывода:

bbe -b "/\x00\x00\xCC\x00\x00\x00/:17" -s -e "F d" -e "p h" -e "A \n" mydata.bin | sed -e 's/x//g'

11:00 00 cc 00 00 00 cd 00 00 00 ce

Ваше решение с Perl из EDIT3 выдает ошибку «Недостаточно памяти» с большими файлами.

Та же проблема связана с bgrep .

Единственным недостатком bbe является то, что я не знаю, как печатать контекст, предшествующий сопоставленному шаблону.

8 голосов
/ 21 февраля 2011

Один из способов решения вашей непосредственной проблемы с использованием только grep - создать файл, содержащий один нулевой байт. После этого grep -abo -f null_byte_file target_file выдаст следующий вывод.

0:
1:
2:
3:
8:
11:

Это, конечно, смещение каждого байта по запросу "-b", за которым следует нулевой байт по запросу "-o"

Я буду первым, кто защитит Perl, но в этом случае нет необходимости привлекать расширенную семью.

1 голос
/ 15 ноября 2010

А как насчет grep -a? Не уверен, как он работает с действительно двоичными файлами, но хорошо работает с текстовыми файлами, которые ОС считает двоичными.

...