2010-09-20 73 views
45

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

数据我试图得到的是所有的ASCII无论如何(在图书馆等参考),它只是不会被发现,因为有时有两个字符之间有00,有时并没有。

我没有看到任何方式让它在语义上完成,但这些00应该做的伎俩,除非我不能轻松地在命令行上使用它们。

+0

......如果字符长度是两个字节,它不是ASCII。 – 2010-09-20 15:28:27

+0

我的意思是ASCII字符范围(U + 0000到U + 007F),而不是ASCII编码。 – taw 2010-09-20 20:27:02

回答

51

最简单的方法是只转换文本文件为UTF-8和管道将到grep:

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

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

似乎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"'` 

它是如何工作的?那么它将您的文件转换为十六进制(不需要任何额外的格式,通常使用hexdump)。它将其转换为grep。 Grep使用通过将你的查询(不带换行符)回显到iconv中的查询来将它转换为utf-16。然后将其输入sed以删除BOM(用于确定排序的utf-16文件的前两个字节)。然后将其传送到hexdump中,以便查询和输入相同。

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

EDIT2:明白了!!!!

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

+1

'iconv'不会起作用,因为它是一个非二进制文件,很多非UTF-16数据,'iconv'在第一个错误时退出。 – taw 2010-09-24 13:27:40

+0

哎哟......我还在寻找到给grep的一个UTF-16查询出于好奇心(我不认为它的转换,因为它并没有真正知道的编码,它得是做别的事情奇怪)和我如果我想出点什么,我会让你知道的。 – 2010-09-24 14:23:09

+0

看看我的编辑。有什么工作。 – 2010-09-24 15:58:57

0

战略经济对话语句是超过了我能绕到我的头在字符串Test(以UTF-16)的十六进制版本。我有一个简单的,远从完美的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" 
    } 
} 
4

我用这一个所有的时间倾倒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" 
+0

我想这种方式有误报的机会渺茫,但它可能什么都想的99.9%。它也适用于MINGW64 Git Bash。 – mwfearnley 2017-07-10 14:45:31

0

我将此添加为上面接受的答案的评论,但为了便于阅读。这使您可以搜索一堆文件中的文本,同时显示正在查找文本的文件名。所有这些文件都有.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 
8

可明确包含在搜索字符串中的空白(00秒),但你会得到的结果与空值,所以你可能需要将输出重定向到一个文件,以便你可以看看它有一个合理的编辑器,或者通过sed管道来替换空值。要搜索* .utf16.txt“吧”:

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

“-P”是告诉grep来接受Perl的正则表达式的语法,这使得\ X00扩展到零,和-a告诉它忽略Unicode看起来像二进制的事实。

+0

好技术,我没想到这个。 grep的'-a'标志在这里是非常神奇的。假定你没有大文件要搜索(在这种情况下这可能太慢),只需指定'.'而不是'\ x00',就可以使输入变得更简单。 '.'将匹配任何内容,而不仅仅是空值。这可能并不总是你想要的,但可能大部分时间都可以。通常,清除空值的sed也不是必需的 - 它们不会输出任何内容。所以对于你的例子,只需'grep -a b.a.r * .utf16.txt'应该可以工作。 – 2015-12-23 22:11:41

4

我需要递归地做到这一点,这是我想出了:

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 

Bash loop;对于文件路径列表中的每一行,将路径放入$l并在循环中执行该操作。 (为什么我使用shell循环而不是xargs,这会更快:我需要在输出的每一行前加上当前文件的名称。如果我正在喂食,想不到这样做的方法一次多个文件的iconv,并且因为我将要在同一时间做反正一个文件,外壳环是比较容易的语法/转义)

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

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

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

这是一个黑客:nl插入行号,却偏偏有一个“使用任意字符串来分隔行数”参数,所以我把文件名(后跟冒号和空格)在那里面。然后我使用cut去除行号,只留下文件名前缀。 (为什么我没有使用sed:这种方式更容易转义,如果我使用sed表达式,我不得不担心文件名中有正则表达式字符,在我的情况下有很多。nl是多少比sed,并且将只取参数-s完全从字面上看,和外壳处理逃逸我。)

因此,通过这条管道的终点,我已经转换一堆文件为UTF-8的线,以文件名为前缀,然后我grep。如果有匹配,我可以从前缀中知道他们在哪个文件中。

注意事项

  • 这是多少,比grep -R慢得多,因为我产卵iconvnlcut,并grep新副本的每一个文件。这太糟糕了。
  • 的一切,是不是UTF-16LE输入会出来为完整的垃圾,所以如果有包含“somestring”正常的ASCII文件,该命令将不会报告它 - 你需要做一个正常的grep -R以及作为这个命令(如果你有多个unicode编码类型,比如一些big-endian和一些little-endian文件,你需要调整这个命令并为每个不同的编码重新运行它)。
  • 文件的名字恰好包含“somestring”将在输出中显示,即使其内容没有匹配。
+0

完全恶心。和veeeery有帮助。 thx – 2017-03-24 12:05:32

+0

我不得不做'找。 -type f'在OS X上 – 2017-08-24 22:39:34

0

我发现下面的解决方案为我工作最好的,从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