標準grep
/pcregrep
などは、ASCIIまたはUTF8データのバイナリファイルで便利に使用できます-UTF16も試す簡単な方法はありますか(できれば同時に、代わりに行います)?
とにかく、取得しようとしているデータはすべてASCIIです(ライブラリ内の参照など)。2つの文字の間に00がある場合とない場合があるため、見つかりません。
意味的にそれを行う方法はわかりませんが、コマンドラインで簡単に使用できないことを除けば、これらの 00 でうまくいくはずです。
最も簡単な方法は、テキストファイルを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
これにより、ファイル内の文字列の16進バージョンTest
(utf-16)が検索されますtest.txt
検索文字列に null (00) を明示的に含めることができますが、null を含む結果が得られます。出力をファイルにリダイレクトして、適切なエディターで表示したり、sed を介してパイプしたりすることができます。ヌルを置き換えます。*.utf16.txt で「bar」を検索するには:
grep -Pa "b\x00a\x00r" *.utf16.txt | sed 's/\x00//g'
"-P" は、\x00 を null に展開できる Perl 正規表現構文を受け入れるように grep に指示し、-a は、Unicode がバイナリのように見えるという事実を無視するように指示します。
私はこれを再帰的に行う必要がありました.これが私が思いついたものです:
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 ではないため、多くのエラーが発生します)。この変換からの出力は stdout に送られます。
nl -s "$l: " | cut -c7-
これはハックです:nl
行番号を挿入しますが、たまたま「この任意の文字列を使用して番号を行から分離する」パラメーターがあるため、ファイル名 (コロンとスペースが続きます) をその中に入れます。次にcut
、ファイル名のプレフィックスだけを残して、行番号を取り除きます。(私が使用しなかった理由: エスケープは、この方法ではるかにsed
簡単です。sed 式を使用した場合、ファイル名に正規表現文字が含まれていることを心配する必要があります。パラメータを完全に文字通りに取るだけで、シェルがエスケープを処理します。)nl
sed
-s
したがって、このパイプラインの終わりまでに、一連のファイルを utf-8 の行に変換し、ファイル名のプレフィックスを付けて grep しました。一致するものがあれば、プレフィックスからどのファイルにあるのかがわかります。
注意事項
grep -R
の新しいコピーを作成しているため、よりもはるかに遅くなります。恐ろしいです。iconv
nl
cut
grep
grep -R
同様に通常のコマンドを実行する必要があります (また、ビッグ エンディアン ファイルとリトル エンディアン ファイルのように、複数の Unicode エンコーディング タイプがある場合は、このコマンドを調整して、異なるエンコーディングごとに再度実行する必要があります)。sed ステートメントは、私が理解できる範囲を超えています。私は単純で完璧とはほど遠い TCL スクリプトを持っていますが、これは 1 つのテスト ポイントで問題なく機能すると思います。
#!/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 のワンライナーを使用できます。
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