Это запрошенное однострочное решение (для последних оболочек, которые имеют «подстановку процессов»):
grep -o "ef be ad de" <(hexdump -v -e '/1 "%02x "' infile.bin) | wc -l
Если «подстановка процесса» <(…)
недоступна, просто используйте grep в качестве фильтра:
hexdump -v -e '/1 "%02x "' infile.bin | grep -o "ef be ad de" | wc -l
Ниже приведено подробное описание каждой части решения.
Значения байтов из шестнадцатеричных чисел:
Ваша первая проблема легко решается:
Эти escape-последовательности \ Xnn работают только в оболочке рыбы.
Измените верхний X
на нижний x
и используйте printf (для большинства оболочек):
$ printf -- '\xef\xbe\xad\xde'
Или используйте:
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
Для тех оболочек, которые решили не реализовывать представление '\ x'.
Конечно, перевод hex в восьмеричное будет работать (почти) в любой оболочке:
$ "$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"
Но это также может быть преобразовано. Ниже приведен пример кругового обхода hex-bin-hex:
$ 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 unbuffered ( -u
) и используется только для вставки двух новых строк в поток для каждой соответствующей строки. Вторая sed
напечатает только (короткие) совпадающие строки. Wc -l будет считать соответствующие строки.
Это буферизует только некоторые короткие строки. Соответствующая строка (и) во втором седе. Это должно быть довольно мало используемых ресурсов.
Или, несколько сложнее понять, но та же идея в одном седе:
a='ef be ad de'
hexdump -v -e '/1 "%02x "' infile.bin |
sed -u '/\n/P;//!s/'"$a"'/\n&\n/;D' |
wc -l
grep -o