Подтвердить что ты не робот

Бинарный grep для Linux?

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

# 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-like.

 

Лучшее, что я могу сделать до сих пор:

$ 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 между байтами ' a '(97) и' b '(98)?

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

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

EDIT3: благодаря ответу @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 очень хороший объект двоичного grepping, я должен признать:) До тех пор, пока вы правильно изучаете синтаксис:)

4b9b3361

Ответ 1

Вход с одним лайнером

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

% 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);

Выход с одним лайнером

BTW, чтобы создать тестовый входной файл, я не использовал ваш большой, длинный Python script; Я просто использовал этот простой однострочный 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 поддерживает несколько способов сделать что-то, чтобы вы могли выбрать тот, который вам больше всего нравится. Если бы это было что-то, что я планировал проверить в качестве школьного или рабочего проекта, я бы выбрал более длинные, более тщательные версии - или, по крайней мере, поместил комментарий в оболочку script, если бы я использовал однострочные.

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

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

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

Ответ 2

Кажется, это работает для меня:

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>

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

Ответ 3

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

Ответ 4

Программа 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 является то, что я не знаю, как печатать контекст, предшествующий сопоставленному шаблону.

Ответ 5

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

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

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

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

Ответ 6

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