grep
/pcregrep
工具可以方便地用于 ASCII 或 UTF8 数据的二进制文件 - 是否有一种简单的方法让它们也尝试使用 UTF16(最好同时进行,但单独进行也可以)?我要获取的数据无论如何都是 ASCII 的(在库引用等中),只是有时会在任意两个字符之间出现 00,有时则没有被发现。
我没有看到任何语义上解决它的方法,但这些 00 应该可以解决问题,只是我不能轻松地在命令行上使用它们。
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
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 -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
中搜索以utf-16编码的字符串Test
的十六进制版本。
iconv
是一个二进制文件,其中有许多非UTF-16数据,并且iconv
会在第一个错误时退出,所以它无法正常工作。 - tawpcregrep \
echo -n "test" | iconv -f utf-8 -t utf-16le | hexdump -e '/1 "x%02x"' | sed 's/x/\\x/g'` <binary.file`。最重要的是,它不需要utf-16字符在2字节边界上 - 所有以前的方法都存在很大问题。甚至可以与“-i”一起使用。 - taw我发现下面的解决方案最适合我,来源于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
你可以在搜索字符串中明确包含空值(00),尽管你将得到带有空值的结果,因此你可能想将输出重定向到文件中,以便你可以使用合理的编辑器查看它,或者通过sed管道替换空值。要在*.utf16.txt中搜索"bar":
grep -Pa "b\x00a\x00r" *.utf16.txt | sed 's/\x00//g'
"-P"告诉grep接受Perl正则表达式语法,允许 \x00 扩展为空值,而 "-a" 告诉它忽略Unicode看起来像二进制的事实。
grep
的 -a
标志。假设您没有要搜索的大文件(在这种情况下可能会太慢),您可以通过仅指定“.”而不是“\x00”来使它更容易输入。 “.”将匹配任何内容,而不仅仅是空值。那可能并不总是你想要的,但大多数情况下都没问题。通常,清除空值的 sed 也是不必要的-它们不会在输出上打印任何内容。因此,对于您的示例,只需使用 grep -a b.a.r *.utf16.txt
即可。 - Dan Prittsripgrep
使用ripgrep
实用工具来搜索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
。我经常在导出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"
sed -ne "s/\x00//g" -e "/192.168/p"- Firstrock
ugrep (通用grep) 全面支持Unicode、UTF-8/16/32输入文件,检测无效的Unicode以确保正确结果,显示文本和二进制文件,并且速度快且免费:
ugrep搜索UTF-8/16/32输入和其他格式。选项
--encoding
允许搜索许多其他文件格式,例如ISO-8859-1到16、EBCDIC、代码页437、850、858、1250到1258、MacRoman和KOI8。
有关详细信息,请参见GitHub上的ugrep。
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小端,并将其转换为utf-8。使用-s
参数避免iconv报告任何转换错误(因为此目录结构中的某些文件不是utf-16格式)。该转换的输出结果将输出到标准输出流(stdout)。
nl -s "$l: " | cut -c7-
nl
命令可以插入行号,不过它恰好有一个“使用任意字符串来分隔数字和行”的参数,所以我把文件名(后面加上冒号和空格)放在那里。然后我使用cut
命令删除行号,只留下文件名前缀。(为什么我没有用sed
呢:因为通过这种方式转义更容易。如果我使用了sed表达式,就必须担心文件名中的正则表达式字符,而我的情况中有很多这样的字符。nl
比sed
要简单得多,它只会完全按照参数-s
给定的文本进行操作,shell会帮我处理转义。)grep -R
要慢得多,因为我为每个文件都启动了新的iconv
、nl
、cut
和grep
进程。非常可怕。grep -R
和这个命令(如果你有多种unicode编码类型,比如一些大端和一些小端文件,则需要调整这个命令并为每种不同的编码再次运行它)。find . -type f
。 - Jake Brownson// 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
这个 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"
}
}
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
{ printf "\xFF\xFE"; cat my-utf16-no-bom.txt; } | grep ...
- Sandburg