查找块设备上出现的正则表达式(行长度缓冲区问题)

查找块设备上出现的正则表达式(行长度缓冲区问题)

我试图查找两个块设备上以字符开头#、介于 1635700000 和 1653699999 之间、以空字符 ( \0) 或 Linux 换行符 ( \0xA) 结尾的所有数字。

我想出的这个grep肯定不优雅:

grep --only-matching --byte-offset --text -Pa '#1635[7-9][0-9]{5}(\x0|$)|#163[6-9][0-9]{6}(\x0|$)|#164[0-9]{7}(\x0|$)|#165[0-2][0-9]{6}(\x0|$)|#1653[0-6][0-9]{5}(\x0|$)' /dev/device

尽管不能像这样输入和执行它,但这里有相同的语句和一些换行符,以使其更具可读性。

grep --only-matching --byte-offset --text -Pa '
 #1635[7-9][0-9]{5}(\x0|$)
|#163[6-9][0-9]{6}(\x0|$)
|#164[0-9]{7}(\x0|$)
|#165[0-2][0-9]{6}(\x0|$)
|#1653[0-6][0-9]{5}(\x0|$)
' /dev/device

这在其中一个块设备上有效,但在另一个块设备上,在一些但不是全部输出之后,它停止并出现错误:

grep: exceeded PCRE's line length limit

我猜测失败的块设备有较长的字节,没有\0or\0xA字符,超过了行长度限制阈值。

因此,我尝试将 NULL 字符更改为换行符:

sed 's/\x0/\n/g' /dev/device | grep ...

但是,它因大约相同的原因而停止:

sed: regex input buffer length larger than INT_MAX

如何在第二个块设备上找到我想要查找的内容?很确定它需要是一个不同的实用程序,要么使用更大的输入缓冲区,要么不读取整行,甚至可能是一个自定义的 perl/python/C/C++ 程序。

我确实需要输出为每个找到的匹配一行,包括找到的字节偏移量和数量。

修改块设备不是一个选项。将会有数以万计的结果,因此在十六进制编辑器之类的工具中手动搜索也不是一个选择。

答案1

在上面的评论中,@terdon 给出了有关首先减少搜索空间的关键见解。通过使用扩展的 grep 模式语法来减少 Perl (PCRE) grep 模式语法的最大行长度,我能够让它工作。

grep --only-matching --byte-offset --text -E '#[0-9]{10}.' /dev/device | grep --only-matching --text -P '[0-9]*:#1635[7-9][0-9]{5}(\x0|$)|[0-9]*:#163[6-9][0-9]{6}(\x0|$)|[0-9]*:#164[0-9]{7}(\x0|$)|[0-9]*:#165[0-2][0-9]{6}(\x0|$)|[0-9]*:#1653[0-6][0-9]{5}(\x0|$)' /dev/device

尽管不能像这样输入和执行它,但这里有相同的语句和一些换行符,以使其更具可读性。

grep --only-matching --byte-offset --text -E 
   '#[0-9]{10}.'
   /dev/device
| grep --only-matching --text -P '
    [0-9]*:#1635[7-9][0-9]{5}(\x0|$)
   |[0-9]*:#163[6-9][0-9]{6}(\x0|$)
   |[0-9]*:#164[0-9]{7}(\x0|$)
   |[0-9]*:#165[0-2][0-9]{6}(\x0|$)
   |[0-9]*:#1653[0-6][0-9]{5}(\x0|$)
   ' /dev/device

扩展的 grep 模式语法引擎没有我遇到的行长度限制,并且减少了给予 perl (PCRE) 模式语法引擎的最大行长度。

答案2

以下是使用 bash(1)、grep(1) 和 perl(1) 的解决方案:

 1  #!/bin/bash
 2  grep -P -a -b -o '#\d{10}(\000|$)' \
 3  | perl -ne '/(\d{10})/; print if 1635700000 <= $1 && $1 <= 1653699999' \
 4  | perl -pe 'chop; /\000/ ? do {chop; $_ .= "\\000\n"} : do {$_ .= "\\n\n"}'

第 1 行告诉 shell 这是一个 Bash 脚本。

第 2-4 行形成命令管道。

第 2 行调用 grep(1):

  • 选项“-P”指定该模式应解释为 Perl 兼容的正则表达式。

  • 选项“-a”指定二进制输入应作为文本处理。

  • 选项“-b”与“-o”组合指定应在每行输出之前打印匹配部分的字节偏移量。

  • 选项“-o”指定只打印匹配的部分。

  • 参数“#\d{10}($|\000)”是一个正则表达式模式——一个数字符号,后跟十个十进制数字,最后是行尾或 NUL。在 Unix/Linux 上,正则表达式行尾“$”元字符与 ASCII 回车符匹配(问题语句“\0xA”)。当在具有不同换行符编码的平台上运行时,正则表达式可能无法找到 ASCII 回车符(例如 MS-DOS 上的 CR-LF,经典 Mac OS 上的 LF 等)。

第 3 行是 Perl 单行过滤器,仅通过数字部分在所需范围内的行。

第 4 行是 Perl 单行代码,使终止换行符或 NUL 可见。

这是一个示例运行:

52:#1647787407\n
70:#1644931194\n
84:#1651134631\000
154:#1646920743\n

相关内容