Greping 二进制文件和 UTF16

标准 grep/pcregrep等可以方便地与二进制文件一起使用 ASCII 或 UTF8数据-有没有一种简单的方法让他们也尝试 UTF16(最好是同时,但可以代替) ?

我试图获得的数据都是 ASCII (库中的引用等) ,它只是不能被找到,因为有时任何两个字符之间有00,有时没有。

我看不出有任何方法可以从语义上完成它,但这些00应该可以做到这一点,只是我不能轻易地在命令行上使用它们。

61191 次浏览

最简单的方法是将文本文件转换为 utf-8,然后将其导入 grep:

iconv -f utf-16 -t utf-8 file.txt | grep query

我尝试做相反的事情(将查询转换为 utf-16) ,但似乎 grep 不喜欢这样。我觉得可能和 Endianness 有关,但我不确定。

好像 grep 会将一个 utf-16查询转换为 utf-8/ascii:

grep `echo -n query | iconv -f utf-8 -t utf-16 | sed 's/..//'` test.txt

如果 test.txt 是一个 utf-16文件,那么它不会工作,但是如果 test.txt 是 ascii,那么它会工作。我只能得出这样的结论: grep 正在将我的查询转换为 ascii。

编辑: 这里有一个非常非常疯狂的方法,虽然管用,但是不能给你很多有用的信息:

hexdump -e '/1 "%02x"' test.txt | grep -P `echo -n Test | iconv -f utf-8 -t utf-16 | sed 's/..//' | hexdump -e '/1 "%02x"'`

它是怎么工作的?它将你的文件转换成十六进制格式(不需要任何额外的格式,这种格式通常适用)。它把这些输送到 grep。Grep 使用的查询是通过将查询(不带换行符)回显到 iconv 中来构造的,该查询将查询转换为 utf-16。然后将其导入 sed 以删除 BOM (utf-16文件的前两个字节用于确定 endianness)。然后通过管道将其输入到 hexdump 中,这样查询和输入就是相同的。

不幸的是,如果有一个匹配项,我认为这将最终打印出整个文件。另外,如果二进制文件中的 utf-16与计算机中的 endianness 不同,那么这种方法也不会起作用。

编辑2: 找到了! ! !

grep -P `echo -n "Test" | iconv -f utf-8 -t utf-16 | sed 's/..//' | hexdump -e '/1 "x%02x"' | sed 's/x/\\\\x/g'` test.txt

这将在文件 test.txt中搜索字符串 Test的十六进制版本(在 utf-16中)

Sed 语句比我想象的要复杂得多。我有一个简单的,远非完美的 TCL 脚本,我认为这个脚本在我的测试点1上做得不错:

#!/usr/bin/tclsh


set insearch [lindex $argv 0]


set search ""


for {set i 0} {$i<[string length $insearch]-1} {incr i} {
set search "${search}[string range $insearch $i $i]."
}
set search "${search}[string range $insearch $i $i]"


for {set i 1} {$i<$argc} {incr i} {
set file [lindex $argv $i]
set status 0
if {! [catch {exec grep -a $search $file} results options]} {
puts "$file: $results"
}
}

在转储 Windows 注册表之后,我一直在使用它,因为它的输出是 unicode。这是在 Cygwin 名下运行的。

$ regedit /e registry.data.out
$ file registry.data.out
registry.data.out: Little-endian **UTF-16 Unicode text**, with CRLF line terminators


$ sed 's/\x00//g' registry.data.out | egrep "192\.168"
"Port"="192.168.1.5"
"IPSubnetAddress"="192.168.189.0"
"IPSubnetAddress"="192.168.102.0"
[HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Print\Monitors\Standard TCP/IP Port\Ports\192.168.1.5]
"HostName"="192.168.1.5"
"Port"="192.168.1.5"
"LocationInformation"="http://192.168.1.28:1215/"
"LocationInformation"="http://192.168.1.5:80/WebServices/Device"
"LocationInformation"="http://192.168.1.5:80/WebServices/Device"
"StandaloneDhcpAddress"="192.168.173.1"
"ScopeAddressBackup"="192.168.137.1"
"ScopeAddress"="192.168.137.1"
"DhcpIPAddress"="192.168.1.24"
"DhcpServer"="192.168.1.1"
"0.0.0.0,0.0.0.0,192.168.1.1,-1"=""
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\Standard TCP/IP Port\Ports\192.168.1.5]
"HostName"="192.168.1.5"
"Port"="192.168.1.5"
"LocationInformation"="http://192.168.1.28:1215/"
"LocationInformation"="http://192.168.1.5:80/WebServices/Device"
"LocationInformation"="http://192.168.1.5:80/WebServices/Device"
"StandaloneDhcpAddress"="192.168.173.1"
"ScopeAddressBackup"="192.168.137.1"
"ScopeAddress"="192.168.137.1"
"DhcpIPAddress"="192.168.1.24"
"DhcpServer"="192.168.1.1"
"0.0.0.0,0.0.0.0,192.168.1.1,-1"=""
"MRU0"="192.168.16.93"
[HKEY_USERS\S-1-5-21-2054485685-3446499333-1556621121-1001\Software\Microsoft\Terminal Server Client\Servers\192.168.16.93]
"A"="192.168.1.23"
"B"="192.168.1.28"
"C"="192.168.1.200:5800"
"192.168.254.190::5901/extra"=hex:02,00
"00"="192.168.254.190:5901"
"ImagePrinterPort"="192.168.1.5"

我添加了这个作为一个评论接受上述答案,但使它更容易阅读。这使您可以在一组文件中搜索文本,同时显示它正在查找的文本的文件名。所有这些文件都有一个。Reg 扩展名,因为我正在搜索导出的 Windows 注册表文件。换掉就是了。带有任何文件扩展名的 reg。

// Define grepreg in bash by pasting at bash command prompt
grepreg ()
{
find -name '*.reg' -exec echo {} \; -exec iconv -f utf-16 -t utf-8 {} \; | grep "$1\|\.reg"
}


// Sample usage
grepreg SampleTextToSearch

您可以在搜索字符串中显式地包含空值(00) ,但是您将得到带有空值的结果,因此您可能希望将输出重定向到一个文件,以便您可以使用合理的编辑器查看它,或者通过 sed 管道将其替换为空值。在 * 中搜索“ bar”。Utf16.txt:

grep -Pa "b\x00a\x00r" *.utf16.txt | sed 's/\x00//g'

“-P”告诉 grep 接受 Perl regexp 语法,这允许 x00展开为 null,而-a 告诉 grep 忽略 Unicode 在它看来像二进制的事实。

我需要递归地执行这个操作,这就是我得出的结论:

find -type f | while read l; do iconv -s -f utf-16le -t utf-8 "$l" | nl -s "$l: " | cut -c7- | grep 'somestring'; done

这是绝对可怕和非常缓慢的,我确信有一个更好的方法,我希望有人可以改进它-,但我很匆忙: P

作用:

find -type f

给出一个文件名的递归列表,其中包含相对于当前文件的路径

while read l; do ... done

对于文件路径列表中的每一行,将路径放入 $l并在循环中执行。(为什么我使用 shell 循环而不是 xargs,这样会快得多: 我需要用当前文件的名称作为输出的每一行的前缀。如果我一次向 iconv 提供多个文件,我想不出一种方法来做到这一点,而且因为我将一次执行一个文件,所以 shell 循环更容易语法/转义。)

iconv -s -f utf-16le -t utf-8 "$l"

转换在 $l中命名的文件: 假设输入文件是 utf-16 little-endian 并将其转换为 utf-8。-s使 iconv 关闭任何转换错误(将会有很多错误,因为这个目录结构中的一些文件不是 utf-16)。这个转换的输出到 stdout。

nl -s "$l: " | cut -c7-

这是一个技巧: nl插入行号,但是它碰巧有一个“使用这个任意字符串将数字与行号分开”的参数,所以我将文件名(后跟冒号和空格)放入其中。然后我使用 cut去掉行号,只留下文件名前缀。(为什么我没有使用 sed: 这样转义更容易。如果我使用 sed 表达式,我必须考虑文件名中是否有正则表达式字符,在我的例子中,有很多正则表达式字符。nlsed笨得多,它只是完全按照字面意思使用参数 -s,shell 为我处理转义。)

因此,在这个管道的末尾,我已经将一大堆文件转换为带有文件名前缀的 utf-8行,然后对其进行 grep。如果有匹配的话,我可以从前缀判断出是哪个文件。

警告

  • 这比 grep -R慢很多很多,因为我为每个文件都生成了一个新的 iconvnlcutgrep副本。太可怕了。
  • 所有不是 utf-16le 输入的东西都会变成完全的垃圾,所以如果有一个包含‘ some string’的普通 ASCII 文件,这个命令就不会报告它——你需要执行一个普通的 grep -R和这个命令(如果你有多个 unicode 编码类型,比如一些 big-endian 和一些 little-endian 文件,你需要调整这个命令并针对每个不同的编码重新运行它)。
  • 名称碰巧包含“ some string”的文件将显示在输出中,即使它们的内容没有匹配项。

我发现下面的解决方案最适合我,从 https://www.splitbits.com/2015/11/11/tip-grep-and-unicode/

Grep 不能很好地使用 Unicode,但是可以绕过它,

Some Search Term

在 UTF-16文件中,使用正则表达式忽略每个字符的第一个字节,

S.o.m.e. .S.e.a.r.c.h. .T.e.r.m

另外,告诉 grep 将文件视为文本,使用’-a’,最终命令如下所示,

grep -a 'S.o.m.e. .S.e.a.r.c.h. .T.e.r.m' utf-16-file.txt

ripgrep

使用 ripgrep实用程序对 UTF-16文件进行 grep。

Ripgrep 支持在 UTF-8以外的文本编码中搜索文件,例如 UTF-16、 latin-1、 GBK、 EUC-JP、 Shift _ JIS 等。(提供了一些对自动检测 UTF-16的支持。其他文本编码必须与 -E/--encoding flag.一起特别指定)

语法示例:

rg sometext file

要转储所有行,请运行: rg -N . file

您可以使用以下 Ruby 的一行程序:

ruby -e "puts File.open('file.txt', mode:'rb:BOM|UTF-16LE').readlines.grep(Regexp.new 'PATTERN'.encode(Encoding::UTF_16LE))"

为简单起见,可以将其定义为如下 shell 函数:

grep-utf16() { ruby -e "puts File.open('$2', mode:'rb:BOM|UTF-16LE').readlines.grep(Regexp.new '$1'.encode(Encoding::UTF_16LE))"; }

然后,它以类似的方式被使用,比如 grep:

grep-utf16 PATTERN file.txt

资料来源: 如何使用 Ruby 的 readlines.grep 来处理 UTF-16文件?

Ugrep (Universal grep)完全支持 Unicode,UTF-8/16/32输入文件,检测无效 Unicode 以确保正确的结果,显示文本和二进制文件,并且快速和免费:

Ugrep 搜索 UTF-8/16/32输入和其他格式。选项 abc0允许搜索许多其他文件格式,如 ISO-8859-1至16、 EBCDIC、代码页437、850、858、1250至1258、 MacRoman 和 KOI8。

详情请参阅 GitHub 上的 ugrep