Это запрошенное однострочное решение (для последних оболочек, которые имеют «подстановку процесса»):
grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l
Если «подстановка процесса» <(…)
недоступна, просто используйте grep как filter:
hexdump -v -e '/1 "%02x "' infile.bin | grep -o "ef be ad de" | wc -l
Ниже приводится подробное описание каждой части решения.
Первую проблему легко решить:
Эти escape-последовательности \ Xnn работают только в оболочке Fish.
Измените верхний X
на нижний x
и используйте printf (для большинства оболочек):
$ printf -- '\xef\xbe\xad\xde'
Или используйте:
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
Для тех оболочек, которые предпочитают не реализовывать представление '\ x'.
Конечно, преобразование шестнадцатеричного числа в восьмеричное будет работать (почти) в любой оболочке:
$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'
Где «$ sh» - любая (разумная) оболочка. Но правильно процитировать его довольно сложно.
Наиболее надежным решением является преобразование файла и последовательности байтов (обоих) в некоторую кодировку, которая не имеет проблем с нечетными значениями символов, например (новая строка) 0x0A
или (нулевой байт) 0x00
. И тем и другим довольно сложно правильно управлять с помощью инструментов, разработанных и адаптированных для обработки «текстовых файлов».
Преобразование, подобное base64, может показаться допустимым, но оно представляет проблему, заключающуюся в том, что каждый входной байт может иметь до трех выходных представлений в зависимости от того, является ли он первым, вторым или третьим байтом позиции mod 24 (биты).
$ echo "abc" | base64
YWJjCg==
$ echo "-abc" | base64
LWFiYwo=
$ echo "--abc" | base64
LS1hYmMK
$ echo "---abc" | base64 # Note that YWJj repeats.
LS0tYWJjCg==
Вот почему наиболее надежным преобразованием должно быть преобразование, которое начинается на границе каждого байта, как в простом HEX-представлении.
Мы можем получить файл с шестнадцатеричным представлением файла любым из следующих инструментов:
$ od -vAn -tx1 infile.bin | tr -d '\n' > infile.hex
$ hexdump -v -e '/1 "%02x "' infile.bin > infile.hex
$ xxd -c1 -p infile.bin | tr '\n' ' ' > infile.hex
Последовательность байтов для поиска в данном случае уже шестнадцатеричная.
:
$ var="ef be ad de"
Но это также может быть преобразовано. Пример передачи шестнадцатеричного-двоичного-шестнадцатеричного цикла туда и обратно:
$ echo "ef be ad de" | xxd -p -r | od -vAn -tx1
ef be ad de
Строка поиска может быть установлена из двоичного представления. Любой из трех представленных выше вариантов od, hexdump или xxd эквивалентен. Просто убедитесь, что вы включили пробелы, чтобы обеспечить совпадение на границах байтов (сдвиг на полубайты не разрешен):
$ a="$(printf "\xef\xbe\xad\xde" | hexdump -v -e '/1 "%02x "')"
$ echo "$a"
ef be ad de
Если двоичный файл выглядит следующим образом:
$ cat infile.bin | xxd
00000000: 5468 6973 2069 7320 efbe adde 2061 2074 This is .... a t
00000010: 6573 7420 0aef bead de0a 6f66 2069 6e70 est ......of inp
00000020: 7574 200a dead beef 0a66 726f 6d20 6120 ut ......from a
00000030: 6269 0a6e 6172 7920 6669 6c65 2e0a 3131 bi.nary file..11
00000040: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000050: 3232 3131 3232 3131 3232 3131 3232 3131 2211221122112211
00000060: 3232 0a
Затем простой поиск с помощью grep выдаст список совпадающих последовательностей:
$ grep -o "$a" infile.hex | wc -l
2
Все это можно выполнить в одной строке:
$ grep -o "ef be ad de" <(xxd -c 1 -p infile.bin | tr '\n' ' ') | wc -l
Например, для поиска 11221122
в том же файле потребуются следующие два шага:
$ a="$(printf '11221122' | hexdump -v -e '/1 "%02x "')"
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ') | wc -l
4
Чтобы "увидеть" совпадения:
$ grep -o "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
3131323231313232
3131323231313232
3131323231313232
3131323231313232
$ grep "$a" <(xxd -c1 -p infile.bin | tr '\n' ' ')
… 0a 3131323231313232313132323131323231313232313132323131323231313232 313132320a
Есть опасения, что grep будет буферизовать весь файл, и, если файл большой, создаст большой . Для этого мы можем использовать небуферизованное решение sed:
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -ue 's/\('"$a"'\)/\n\1\n/g' |
sed -n '/^'"$a"'$/p' |
wc -l
Первый sed не буферизован ( -u
) и используется только для вставки двух новых строк в поток для каждой соответствующей строки. Второй sed
будет печатать только (короткие) совпадающие строки. Wc -l подсчитает совпадающие строки.
Это приведет к буферизации только некоторых коротких строк. Соответствующая строка (строки) во втором sed. Это должно быть довольно мало используемых ресурсов.
Или, что несколько сложнее для понимания, но та же идея в одном sed:
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
wc -l