我正在尝试查找文件中十六进制模式的偏移量。这适用于一个特定值:
$ grep -obUaP -m1 "\x00\x50\x53\x46\x01\x01\x00\x00\x34\x01\x00\x00" file.bin
3088:PSF4
但是,此模式包含一些会更改的字节,因此我需要在 grep 中包含通配符。我不知道该怎么做。这是迄今为止我尝试过的所有内容:
\x.., \x., ..,
我能想到的每一个类似的形式都不匹配\x[0-9][0-9]
不匹配\x.*
不匹配- 只是
.*
(即,\x00.*\x01
)做匹配,但它是贪婪的并且比模式匹配更多
可能忽略了一些愚蠢的事情,但我在这里碰壁了。
如何指定十六进制通配符,或者至少在使用 grep 和 perl-regex 来搜索十六进制时?
答案1
grep -P '\xAB'
不寻找十六进制字符。不存在这样的事情十六进制字符。\xAB
是PCRE句法匹配以十六进制表示的代码点值为 0xAB(十进制为 171)的字符。
代码点这里将是使用 UTF-8 的语言环境中的 Unicode 代码点和使用单字节字符集的语言环境中的字节值(GNUgrep -P
不支持除 UTF-8 之外的多字节字符集)。
因此,\xAB
将匹配 UTF-8 语言环境中的 U+00AB 字符 («)(该字符以 2 个字节进行编码:0xc2 和 0xab)以及单字节语言环境中的 0xAB 字节(例如,它Ћ
表示使用 iso8859-5 字符集的区域设置)。
如果您想匹配字节值,您应该确保区域设置使用单字节字符集,C
区域设置可能是您最好的选择。
LC_ALL=C grep -P '\xAB'
匹配 0xAB (171) 字节,无论它在任何字符集中代表什么字符(如果有)。
要匹配任何单个字节,您可以再次使用.
(假设 C 语言环境或任何每个字符字符集为单个字节的本地语言)。
匹配某个范围内的字节值,正如 @Angle115 已经说过的:([\x01-\x45]
此处适用于字节值 1 到 0x45 / 69)
但请记住,grep
匹配内容文本的线¹,因此它永远不会找到作为行分隔符的换行符,并且无论区域设置如何,其值始终为 0x0A²(十进制为 10)。
因此LC_ALL=C grep -P '\x23.\xab'
将匹配 3 个字节的序列,第一个字节的值为 0x23,第二个字节的值为任意值除了 0xA第三个值为 0xAB。
为了能够搜索具有任意值(包括 0xA)的字节,您需要将整个输入视为一个整体,而不是像以前那样一次处理一行或 nul 分隔的记录grep
。
为此,您可以将pcregrep
其-M
(多行)选项与(?s)
标志一起使用(换行符不被 特殊处理.
)或perl
与其 slurp-mode 一起使用:
LC_ALL=C pcregrep --file-offsets -Ma '(?s)\x23.\xab' < file
(pcregrep
没有-b
选项,--file-offsets
它打印偏移量和长度可能是最接近的)。
perl -l -0777 -ne 'print "$-[0]:$_" while /\x23.\xab/gs' < file
或者:
perl -l -0777 -ne 'print $-[0] if /\x23.\xab/s' < file
仅打印第一个匹配的字节偏移量。
perl
将整个文件加载到内存中,pcregrep
不会,但有内部限制,可能会阻止您处理 0xA 字节相距较远的文件。
--null
¹ 或用/分隔的 NUL 记录-z
¹ 在基于 ASCII 的系统上。我什至不知道 libpcre 是否曾经被移植到 EBCDIC 系统,我怀疑现在很多人都不会遇到其中的一些。
答案2
您可以包含范围而不是使用通配符来匹配所有 ASCII 字符,如下所示:
grep -Pe '\x00\x50\x53\x46[\x00-\x7F]\x01\x00\x00\x34\x01\x00\x00'