2010-09-20 32 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