web-dev-qa-db-ja.com

バイナリファイルとUTF16のgrepping

標準のgrep/pcregrepなどは、ASCIIまたはUTF8データのバイナリファイルで便利に使用できます。UTF16も試してみる簡単な方法はありますか(できれば同時に、しかし代わりにそうしますか?

取得しようとしているデータはすべてASCIIとにかく(ライブラリなどの参照)です。2つの文字の間に00があり、時にはないので、見つかりません。 。

意味論的にそれを達成する方法はわかりませんが、コマンドラインで簡単に使用できないことを除いて、これらの00はトリックを行う必要があります。

61
taw

最も簡単な方法は、テキストファイルを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"'`

どのように機能しますか?さて、ファイルを16進数に変換します(hexdumpが通常適用する追加の書式設定はありません)。それをgrepにパイプします。 Grepは、クエリを(改行なしで)iconvにエコーして作成したクエリを使用して、utf-16に変換します。次に、これはsedにパイプされてBOM(utf-16ファイルの最初の2バイトがエンディアンを判別するために使用されます)を削除します。次に、これはhexdumpにパイプされるため、クエリと入力は同じになります。

残念ながら、一致するものが1つしかない場合は、これによりファイル全体が出力されると思います。また、バイナリファイル内の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内の文字列Test(utf-16内)の16進バージョンを検索します

64
Niki Yoshiuchi

検索文字列に明示的にnull(00)を含めることができますが、nullの結果が得られるため、出力をファイルにリダイレクトして、妥当なエディターで表示したり、sedにパイプしたりできます。ヌルを置き換えます。 * .utf16.txtで「bar」を検索するには:

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

「-P」は、grepにPerl正規表現構文を受け入れるように指示します。これにより、\ x00をヌルに展開できます。-aは、Unicodeがバイナリに見えるという事実を無視するように指示します。

14
Ethan Bradford

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
9
nirmal

出力はユニコードであるため、Windowsレジストリをダンプした後、これを常に使用します。これは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"
5
Mike Cush

私はこれを再帰的に行う必要がありましたが、ここに私が思いついたものがあります:

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に入れて、ループ内で処理を行います。 (xargsの代わりにシェルループを使用した理由は、はるかに高速でした。出力の各行の前に現在のファイルの名前を付ける必要があります。フィードする場合、それを行う方法は考えられませんでした。一度に複数のファイルをiconvに送信します。とにかく一度に1つのファイルを実行するため、シェルループの方が構文/エスケープが簡単です。)

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

$lで指定されたファイルを変換します。入力ファイルがutf-16リトルエンディアンであると想定し、utf-8に変換します。 -sは、変換エラーについてiconvを停止させます(このディレクトリ構造内の一部のファイルはutf-16ではないため、多くのエラーが発生します)。この変換の出力は標準出力に送られます。

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

これはハックです:nlは行番号を挿入しますが、「この任意の文字列を使用して行と番号を区切る」パラメーターがあるため、その中にファイル名(コロンとスペースが続きます)を入れます。次に、cutを使用して、行番号を取り除き、ファイル名のプレフィックスのみを残します。 (なぜsedを使用しなかったのか:エスケープはこの方法ではるかに簡単です。sed式を使用した場合、ファイル名に正規表現文字が含まれていることを心配する必要があります。 nlsedよりもかなりひどく、パラメーター-sを完全にそのまま使用します。シェルはエスケープを処理します。)

そのため、このパイプラインの終わりまでに、一連のファイルをutf-8の行に変換し、その前にファイル名を付けてgrepしました。一致するものがある場合、プレフィックスからどのファイルにあるかを確認できます。

注意事項

  • これはgrep -Rよりもはるかに遅いです。なぜなら、すべてのファイルに対してiconvnlcut、およびgrepの新しいコピーを作成しているからです。 。恐ろしいです。
  • Utf-16leの入力ではないものはすべて完全なゴミとして出力されるため、「somestring」を含む通常のASCIIファイルがある場合、このコマンドはそれを報告しません-通常の操作を行う必要がありますgrep -Rとこのコマンド(およびビッグエンディアンファイルとリトルエンディアンファイルのように複数のUnicodeエンコードタイプがある場合、このコマンドを調整し、エンコードごとに再実行する必要があります)。
  • 名前に「somestring」が含まれているファイルには、たとえ内容が一致していなくても、出力に表示されます。
4
Felix

ripgrep

ripgrep utility を使用して、UTF-16ファイルをgrepします。

ripgrepは、UTF-16、latin-1、GBK、EUC-JP、Shift_JISなど、UTF-8以外のテキストエンコーディングのファイルの検索をサポートしています。 (UTF-16の自動検出の一部のサポートが提供されます。他のテキストエンコーディングは、-E/--encoding flag.

構文例:

rg sometext file

すべての行をダンプするには、次を実行します:rg -N . file

2
kenorb

上記の受け入れられた答えにコメントとしてこれを追加しましたが、読みやすくするためです。これにより、多数のファイル内のテキストを検索しながら、テキストを検出しているファイル名も表示できます。エクスポートされたWindowsレジストリファイルを検索しているため、これらのファイルにはすべて.reg拡張子が付いています。 .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
0
Andrew Stern

次のRubyのワンライナーを使用できます。

Ruby -e "puts File.open('file.txt', mode:'rb:BOM|UTF-16LE').readlines.grep(Regexp.new 'PATTERN'.encode(Encoding::UTF_16LE))"

簡単にするために、これは次のようなシェル関数として定義できます。

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

ソース: TF-16ファイルにRubyのreadlines.grepを使用する方法

0
kenorb

Sedステートメントは、頭を包み込む以上のものです。私は、テストポイントが1でも問題ないという単純な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"
    }
}
0
user1117791