Рекурсивно искать каталог двоичных файлов для шестнадцатеричной последовательности?

Текущие команды, которые я использую для поиска некоторых шестнадцатеричных значений (скажем, 0A 8b 02) привлекать:

find . -type f -not -name "*.png" -exec xxd -p {} \; | grep "0a8b02" || xargs -0 -P 4

Можно ли улучшить это с учетом следующих целей:

  • поиск файлов рекурсивно
  • отобразить смещение и имя файла
  • исключить определенные файлы с определенными расширениями (приведенный выше пример не будет искать .png файлы)
  • скорость: поиск должен обрабатывать 200 000 файлов (от 50 КБ до 1 МБ) в общей сложности ~2 ГБ.

Я не слишком уверен, если xargs работает нормально для 4 процессоров. Также у меня возникают проблемы с печатью имени файла, когда grep находит совпадение, так как он по трубопроводу xxd, Какие-либо предложения?

1 ответ

Решение

ЕСЛИ:

  • у вас есть GNU grep
  • И шестнадцатеричные байты, которые вы ищете, НИКОГДА не содержат новых строк (0xa) [1]
    • Если они содержат NUL (0x), вы должны предоставить grep поиск строки через файл (-f) а не по прямому аргументу.

следующая команда приведет вас туда, используя пример поиска 0e 8b 02:

LC_ALL=C find . -type f -not -name "*.png" -exec grep -FHoab $'\x{0e}\x{8b}\x{02}' {} + |
  LC_ALL=C cut -d: -f1-2

grep Команда производит выходные строки следующим образом:

<filename>:<byte-offset>:<matched-bytes>

который LC_ALL=C cut -d: -f1-2 затем сводится к <filename>:<byte-offset>

Команда почти работает с BSD grep за исключением того, что сообщаемое смещение в байтах неизменно является началом строки, с которой был сопоставлен шаблон.
Другими словами: смещение байта будет корректным только в том случае, если в файле не будет ни одной новой строки.
Также BSD grep не поддерживает указание NUL (0x0) байт как часть строки поиска, даже если она предоставлена ​​через файл с -f,

  • Обратите внимание, что параллельной обработки не будет, а только несколько grep вызовы, основанные на использовании find "s -exec ... +, который, как xargs, передает столько имен файлов, сколько поместится в командной строке grep однажды.
  • Позволяя grep искать последовательность байтов напрямую, нет необходимости xxd:
    • Последовательность указана в виде строки ANSI C в кавычках, что означает, что escape-последовательности расширяются оболочкой до литералов, что позволяет Grep затем искать результирующую строку как литерал (через -F), что быстрее.
      Связанная статья из bash руководство, но они работают в zsh (а также ksh ) тоже.
      • Альтернативой GNU Grep является использование -P (поддержка PRCE, Perl-совместимых регулярных выражений) с нерасширенными escape-последовательностями, но это будет медленнее: grep -PHoab '\x{0e}\x{8b}\x{02}'
    • LC_ALL=C гарантирует, что grep обрабатывает каждый байт как свой собственный символ без применения каких-либо правил кодирования.
    • -F обрабатывает строки поиска как литерал (а не как регулярное выражение)
    • -H добавляет соответствующее имя входного файла к каждой строке вывода; обратите внимание, что Grep делает это неявно, если задано более 1 аргумента имени файла
    • -o сообщать только о совпадающих строках (байтовых последовательностях), а не о всей строке (в любом случае концепция строки не имеет смысла в двоичных файлах) [2]
    • -a обрабатывает двоичные файлы, как если бы они были текстовыми файлами (без этого Grep будет печатать только текст Binary file <filename> matches для двоичных входных файлов с совпадениями)
    • -b сообщает о смещениях байтов совпадений

Если в данном входном файле достаточно найти не более 1 совпадения, добавьте -m 1,


[1] Новые строки нельзя использовать, потому что Grep неизменно обрабатывает новые строки в строке шаблона поиска как разделение нескольких шаблонов поиска. Кроме того, Grep основывается на строках, поэтому вы не можете найти совпадения между строками; GNU Grep's -null-data может помочь опция разделения ввода на байты NUL, но только если ваша последовательность байтов поиска также не содержит байтов NUL; Вы также должны представить свои байтовые значения как escape-последовательности в регулярном выражении в сочетании с -P - потому что вам нужно будет использовать escape-последовательность \n вместо фактических новых строк.

[2] -o необходимо сделать -b сообщить смещение байтов совпадения в отличие от начала строки (как указано, BSD Grep всегда делает последнее, к сожалению); кроме того, здесь выгодно сообщать только о самих совпадениях, так как попытка напечатать всю строку приведет к непредсказуемо длинным выходным строкам, учитывая, что в двоичных файлах отсутствует понятие строк; в любом случае, вывод байтов из двоичного файла может вызвать странное поведение рендеринга в терминале.

Другие вопросы по тегам