Я хочу посчитать, сколько раз определенная последовательность байтов происходит внутри файла, который у меня есть. Например, я хочу узнать, сколько раз число \0xdeadbeef
встречается в исполняемом файле. Прямо сейчас я делаю это, используя grep:
#/usr/bin/fish
grep -c \Xef\Xbe\Xad\Xde my_executable_file
(Байты записаны в обратном порядке, потому что мой процессор имеет младший порядок)
Однако у меня есть две проблемы с моим подходом:
- Эти
\Xnn
экранирующие последовательности работают только в оболочке рыбы. - grep фактически считает количество строк, содержащих мое магическое число. Если шаблон встречается дважды в одной строке, он будет учитываться только один раз.
Есть ли способ исправить эти проблемы? Как я могу заставить этот лайнер работать в оболочке Bash и точно подсчитать, сколько раз паттерн встречается внутри файла?
bash
grep
escape-characters
hugomg
источник
источник
grep -o
11221122
, что должно быть возвращено на входе, как112211221122
? 1 или 2?Ответы:
Это запрошенное однострочное решение (для последних оболочек, которые имеют «подстановку процессов»):
Если «подстановка процесса»
<(…)
недоступна, просто используйте grep в качестве фильтра:Ниже приведено подробное описание каждой части решения.
Значения байтов из шестнадцатеричных чисел:
Ваша первая проблема легко решается:
Измените верхний
X
на нижнийx
и используйте printf (для большинства оболочек):Или используйте:
Для тех оболочек, которые решили не реализовывать представление '\ x'.
Конечно, перевод hex в восьмеричное будет работать (почти) в любой оболочке:
Где "$ sh" - это любая (разумная) оболочка. Но довольно сложно правильно его процитировать.
Бинарные файлы.
Наиболее надежное решение - преобразовать файл и последовательность байтов (обе) в некоторую кодировку, которая не имеет проблем со значениями нечетного символа, такими как (новая строка)
0x0A
или (нулевой байт)0x00
. И то, и другое довольно сложно правильно использовать с помощью инструментов, разработанных и адаптированных для обработки «текстовых файлов».Преобразование типа base64 может показаться допустимым, но оно представляет проблему, заключающуюся в том, что каждый входной байт может иметь до трех выходных представлений, в зависимости от того, является ли он первым, вторым или третьим байтом позиции mod 24 (биты).
Шестнадцатеричное преобразование
Вот почему наиболее надежное преобразование должно начинаться с границы каждого байта, как простое представление HEX.
Мы можем получить файл с шестнадцатеричным представлением файла любым из следующих инструментов:
В этом случае последовательность байтов для поиска уже находится в шестнадцатеричном формате.
:
Но это также может быть преобразовано. Ниже приведен пример кругового обхода hex-bin-hex:
Строка поиска может быть установлена из двоичного представления. Любые из трех представленных выше вариантов od, hexdump или xxd эквивалентны. Просто убедитесь, что включены пробелы, чтобы убедиться, что совпадение находится на границах байтов (не допускается сдвиг клочка):
Если бинарный файл выглядит так:
Затем простой поиск grep выдаст список совпадающих последовательностей:
Одна линия?
Все это может быть выполнено в одну строку:
Например, для поиска
11221122
в одном и том же файле понадобятся следующие два шага:Чтобы «увидеть» совпадения:
… 0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a
Буферизация
Существует опасение, что grep буферизирует весь файл и, если он большой, создает большую нагрузку на компьютер. Для этого мы можем использовать небуферизованное решение sed:
Первый sed unbuffered (
-u
) и используется только для вставки двух новых строк в поток для каждой соответствующей строки. Втораяsed
напечатает только (короткие) совпадающие строки. Wc -l будет считать соответствующие строки.Это буферизует только некоторые короткие строки. Соответствующая строка (и) во втором седе. Это должно быть довольно мало используемых ресурсов.
Или, несколько сложнее понять, но та же идея в одном седе:
источник
grep
итоге он будет загружен целиком в память (здесь в два раза больше исходного файла + 1 из-за шестнадцатеричной кодировки), так что в итоге он будет более накладные расходы, чемpython
подход илиperl
тот, с-0777
. Вам также нужнаgrep
реализация, которая поддерживает строки произвольной длины (те, которые поддерживают,-o
как правило, делают). Хороший ответ иначе.od -An -tx1 | tr -d '\n'
илиhexdump -v -e '/1 " %02x"'
с помощью строки поиска, также содержащей пробелы, избегайте этого, но я не вижу такого исправления дляxxd
.sed -u
(где доступно) для снятия буфера. Это означает, что он будет считывать по одному байту за раз на входе и сразу выводить свой вывод без буферизации. В любом случае, ему все равно потребуется загрузить всю строку в пространстве шаблона, поэтому здесь это не поможет.В GNU
grep
«s-P
(Perl-регулярных выражений) флагLC_ALL=C
состоит в том, чтобы избежать проблем в многобайтовых локалях, гдеgrep
иначе пытались бы интерпретировать последовательности байтов как символы.-a
рассматривает двоичные файлы как текстовые файлы (вместо обычного поведения, при которомgrep
выводится только то, есть ли хотя бы одно совпадение или нет)источник
grep
их в соответствие?-a
опцию, иначе grep ответитBinary file file.bin matches
за любой файл, который grep обнаружил как двоичный файл.Который обрабатывает входной файл (ы) как двоичный (без перевода для перевода строки или кодирования, см. Perlrun ), затем зацикливается на входном файле (ах), не печатая увеличивая счетчик для всех совпадений данного гекса (или любой другой формы, см. Perlre ) ,
источник
-0ooo
).$/
, с немного другим компромиссом (использование памяти пропорционально максимальному расстоянию между такими последовательностями):perl -nE 'BEGIN { $/ = "\xef\xbe\xad\xde" } chomp; $c++ unless eof && length; END { say $c }'
С GNU
awk
вы можете сделать:Если какой-либо из байтов является оператором ERE, он должен быть экранирован (с помощью
\\
). Как и0x2e
что.
должно быть введено как\\.
или\\\x2e
. Кроме этого, он должен работать с произвольными значениями байтов, включая 0 и 0xa.Обратите внимание, что это не так просто,
NR-1
потому что есть пара особых случаев:RT==""
.Также обратите внимание, что в худшем случае (если файл не содержит поискового запроса), файл будет загружен целиком в память).
источник
Самый простой перевод, который я вижу:
Где я использовал в
$'\xef'
качестве Баша ANSI-цитирование (первоначальноksh93
функции, теперь поддерживаетсяzsh
,bash
,mksh
, FreeBSDsh
) версия рыбы\Xef
, и используетсяgrep -o ... | wc -l
для подсчета экземпляров.grep -o
выводит каждое совпадение на отдельной строке.-a
Флаг делает Grep себя на бинарные файлы так же , как это делает на текстовые файлы.-F
предназначен для фиксированных строк, поэтому вам не нужно экранировать операторы регулярных выражений.Как и в вашем
fish
случае, вы не можете использовать этот подход, хотя, если последовательность поиска включает байты 0 или 0xa (перевод строки в ASCII).источник
printf '%b' $(printf '\\%o ' $((0xef)) $((0xbe)) $((0xad)) $((0xde))) > hugohex'
будет наиболее переносимым методом «чистой оболочки». Конечно,printf "efbeadde" | xxd -p -r > hugohex
кажется наиболее практичным методом.Вы можете использовать
bytes.count
метод Python, чтобы получить общее количество неперекрывающихся подстрок в строке байтов.Этот однострочный загружает весь файл в память, поэтому не самый эффективный, но работает и более разборчив, чем Perl; D
источник
239I$ 190I$ 173I$ 222I$ HXA ERfile$Y 0UC <:S^EQA$; %C$> QC=
(gd & r)mmap()
файл в Python ; это уменьшило бы фиксацию памяти.источник
Я думаю, что вы можете использовать Perl, попробуйте:
Команда Replace
s
дает число выполненных замен, -0777 означает, что новая строка не рассматривается как специальный символ,e
- выполните команду,say
чтобы напечатать то, что идет дальше, затем напечатать символ новой строки,n
я не полностью понял, но не работает без документы:источник