grepping二进制文件和UTF16

标准的grep / pcregrep等可以方便地用于ASCII或UTF8数据的二进制文件 – 是否有一个简单的方法,使他们也尝试UTF16(最好同时,而是会做)?

我试图得到的数据是所有的ASCII无论如何(库中的引用等),它只是不会被发现,因为有时有两个字符之间有00,有时没有。

我看不出有什么方法可以在语义上完成它,但是这些00应该做的伎俩,除非我不能轻易地在命令行上使用它们。

最简单的方法是将文本文件转换为utf-8并将其转换为grep:

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

我试图做相反的(把我的查询转换为utf-16),但似乎grep不喜欢那样。 我认为这可能与sorting有关,但我不确定。

看起来好像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"'` 

它是如何工作的? 那么它将您的文件转换为hex(没有任何额外的格式,通常应用hexdump)。 它将其转换为grep。 Grep正在使用通过将查询(不带换行符)回显到iconv中将其转换为utf-16的查询。 然后将其传送到sed以删除BOM(用于确定字节序的UTF-16文件的前两个字节)。 然后将其传送到hexdump中,以便查询和input相同。

不幸的是,我认为如果只有一个匹配,这将最终打印整个文件。 如果你的二进制文件中的utf-16存储在与你的机器不同的字节顺序中,这也不起作用。

编辑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.txtsearchstringTest (在utf-16)中的hex版本

您可以在searchstring中明确包含空值(00),尽pipe您会得到空值的结果,因此您可能希望将输出redirect到一个文件,以便您可以使用合理的编辑器查看它,或者通过sedreplace空值。 在* .utf16.txt中search“bar”:

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

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

转储Windowsregistry后,我一直使用这一个,因为它的输出是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" 

我需要recursion地做到这一点,下面是我想到的:

 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 

给出了相对于当前path的文件名的recursion列表

 while read l; do ... done 

Bash循环; 对于文件path列表的每一行,把path放到$l然后在循环中进行操作。 (为什么我使用了一个shell循环而不是xargs,这会更快一些:我需要在输出的每一行前面加上当前文件的名字,如果我正在喂食,想不到这样做多个文件一次到iconv,因为我将一次做一个文件,shell循环更容易语法/转义。)

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

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

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

这是一个黑客行为: nl插入行号,但它恰好有一个“使用这个任意的string来分隔数字的行”参数,所以我把文件名(后跟冒号和空格)。 然后我使用cut去掉行号,只留下文件名前缀。 (为什么我没有使用sed :escaping更容易,如果我使用sedexpression式,我不得不担心文件名中有正则expression式字符,在我的情况下有很多。而不是sed ,并且只是简单地使用参数-s ,并且shell会为我处理转义。)

所以,在这个pipe道的末端,我已经把一堆文件转换成utf-8的行,以文件名作为前缀,然后grep。 如果有匹配,我可以从前缀中知道他们在哪个文件中。

注意事项

  • 这比grep -R慢得多,因为我正在为每一个文件生成一个新的iconvnlcutgrep副本。 这太糟糕了。
  • 所有不是utf-16leinput的东西都是完整的垃圾,所以如果有一个正常的ASCII文件包含'somestring',这个命令不会报告 – 你需要做一个正常的grep -R以及这个命令(如果你有多个unicode编码types,比如一些big-endian和一些little-endian文件,你需要调整这个命令并为每个不同的编码重新运行)。
  • 即使内容不匹配,输出中也会显示名称恰好包含“somestring”的文件。

sed声明比我可以包裹我的头。 我有一个简单的,非常完美的TCL脚本,我觉得在我的测​​试点上可以做一个好的工作:

 #!/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" } } 

我添加了这个作为上面的接受的答案的评论,但更容易阅读。 这允许您在一堆文件中search文本,同时显示正在查找文本的文件名。 所有这些文件都有.reg扩展名,因为我正在search导出的Windowsregistry文件。 只要用任何文件扩展名replace.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