这是要求的单线解决方案(对于具有“进程替换”功能的最新Shell):
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
以下是解决方案各部分的详细说明。
十六进制数字的字节值:
您的第一个问题很容易解决:
这些\ Xnn转义序列仅在鱼壳中起作用。
将上部更改X
为下部,x
并使用printf(对于大多数shell):
$ printf -- '\xef\xbe\xad\xde'
或使用:
$ /usr/bin/printf -- '\xef\xbe\xad\xde'
对于那些选择不实现'\ x'表示形式的shell。
当然,将十六进制转换为八进制将在(几乎)任何外壳上工作:
$ "$sh" -c 'printf '\''%b'\'' "$(printf '\''\\0%o'\'' $((0xef)) $((0xbe)) $((0xad)) $((0xde)) )"'
其中“ $ sh”是任何(合理的)shell。但是很难正确引用它。
二进制文件。
最健壮的解决方案是将文件和字节序列(两者)都转换为某种编码,这些编码对于奇数字符值(如新行)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"
但是它也可以改变。往返十六进制-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是无缓冲的(-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
grep -o