grepping二进制文件和UTF16

时间:2010-09-20 15:25:04

标签: unicode grep utf-16

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

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

我没有看到任何方法在语义上完成它,但是这些00应该可以解决问题,除非我不能在命令行上轻松使用它们。

10 个答案:

答案 0 :(得分:64)

最简单的方法是将文本文件转换为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中而构建的,该iconv将其转换为utf-16。然后将其传送到sed以删除BOM(用于确定字节序的utf-16文件的前两个字节)。然后将其传送到hexdump,以便查询和输入相同。

不幸的是,如果只有一个匹配,我认为这将最终打印出整个ENTIRE文件。如果二进制文件中的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

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

答案 1 :(得分:13)

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

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

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

答案 2 :(得分:8)

我发现以下解决方案最适合我,来自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

答案 3 :(得分: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"

答案 4 :(得分: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循环;对于文件路径列表的每一行,将路径放入$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-

这是一个hack:nl插入行号,但碰巧有一个“使用这个任意字符串来分隔行号”参数,所以我把文件名(后跟冒号和空格)在那里面。然后我使用cut去除行号,只留下文件名前缀。 (为什么我没有使用sed:这种方式更容易转义。如果我使用了sed表达式,我不得不担心文件名中有正则表达式字符,在我的例子中有很多。 nlsed更笨,并且只是完全按字面意思取出参数-s,shell会为我处理转义。)

因此,在这个管道的末尾,我已经将一堆文件转换为utf-8行,前缀为文件名,然后我将其grep。如果有匹配,我可以从前缀告诉他们所在的文件。

注意事项

  • 这比grep -R慢很多,因为我正在为iconvnlcutgrep生成新副本每一个文件。太可怕了。
  • 不是utf-16le输入的所有内容都将作为完全垃圾出现,所以如果有一个普通的ASCII文件包含'somestring',这个命令就不会报告它 - 你需要做一个正常的{{1以及这个命令(如果你有多个unicode编码类型,比如一些big-endian和一些little-endian文件,你需要调整这个命令并为每个不同的编码再次运行它。)
  • 名称恰好包含“somestring”的文件将显示在输出中,即使其内容没有匹配项。

答案 5 :(得分:2)

ripgrep

使用ripgrep utility grep UTF-16文件。

  

ripgrep支持以UTF-8以外的文本编码搜索文件,例如UTF-16,latin-1,GBK,EUC-JP,Shift_JIS等。 (提供了一些对自动检测UTF-16的支持。必须使用-E / --encoding flag.特别指定其他文本编码)

语法示例:

rg sometext file

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

答案 6 :(得分:0)

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"
    }
}

答案 7 :(得分: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 :(得分:0)

您可以使用以下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

来源:How to use Ruby's readlines.grep for UTF-16 files?

答案 9 :(得分:0)

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

  

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

Download ugrep from GitHub