grepがファイルをバイナリと見なす理由は何ですか?


185

ボックスにWindowsシステムからのデータベースダンプがいくつかあります。テキストファイルです。私はそれらをgrepするためにcygwinを使用しています。これらはプレーンテキストファイルのようです。メモ帳やワードパッドなどのテキストエディターでそれらを開くと、見やすくなります。ただし、それらに対してgrepを実行すると、と表示されますbinary file foo.txt matches

ファイルにいくつかのアスキーNUL文字が含まれていることに気付きましたが、これはデータベースダンプからのアーティファクトだと思います。

それでは、grepがこれらのファイルをバイナリと見なすのはなぜですか?NUL文字?ファイルシステムにフラグはありますか?grepを取得して行の一致を表示するには、何を変更する必要がありますか?


2
--null-dataNUL区切り文字の場合に役立ちます。
スティーブo

回答:


125

NULファイル内のどこかに文字がある場合、grepはそれをバイナリファイルと見なします。

cat file | tr -d '\000' | yourgrep最初にすべてのヌルを削除してからファイルを検索するこのような回避策があります。


149
...または、少なくともGNU grepでは-a/を使用--textします。
デロバート

1
@derobert:実際、一部の(古い)システムでは、grepは行を表示しますが、その出力は最初に一致する各行を切り捨てますNUL(おそらく、Cのprintfを呼び出して一致した行を与えるためですか?)。このようなシステムでgrep cmd .sh_historyは、sh_historyの各行には各行の先頭にaがある特定の形式があるため、aは 'cmd'に一致する行と同じ数の空行を返しNULます。(ただし、「少なくともGNU grepについて」というコメントはおそらく当てはまります。現在のところ、テストする手元にはありませんが、うまく処理できると期待しています)
オリヴィエデュラック

4
NUL文字の存在が唯一の基準ですか?疑わしい。おそらくそれよりも賢いでしょう。Ascii 32-126の範囲外にあるものはすべて私の推測ですが、ソースコードを確認して確認する必要があります。
マイケルマルティネス

2
私の情報は、特定のgrepインスタンスのmanページからでした。実装に関するあなたのコメントは有効であり、ソースはドキュメントに勝っています。
bbaja42

2
grep通常のASCIIハイフン/マイナス(0x2d)ではなく長いダッシュ(0x96)があるため、cygwinでバイナリと見なされるファイルがありました。この回答でOPの問題は解決したと思いますが、不完全なようです。
cp.engr

121

grep -a 私のために働いた:

$ grep --help
[...]
 -a, --text                equivalent to --binary-files=text

4
これが最良かつ最も安価な回答IMOです。
pydsigner

しかしPOSIX準拠ではありません
Matteo

21

あなたは使用することができますstrings任意のファイルからテキストの内容を抽出して、パイプを経由しユーティリティをgrep次のように、: strings file | grep pattern


2
部分的に破損している可能性のあるログファイルをgrepするのに最適
Hannes R.

はい、時々バイナリ混合ロギングも起こります。これはいい。
sdkks

13

GNU grep 2.24 RTFS

結論:2および2ケースのみ:

  • NUL、例えば printf 'a\0' | grep 'a'

  • C99によるエンコードエラーmbrlen()、例:

    export LC_CTYPE='en_US.UTF-8'
    printf 'a\x80' | grep 'a'
    

    \x80UTF-8 Unicodeポイントの最初のバイトにはできないためです。UTF-8-説明| en.wikipedia.org

さらに、StéphaneChazelasが述べたように、grepがファイルをバイナリと見なす理由は何ですか?| Unix&Linux Stack Exchange、これらのチェックは、TODOの長さの最初のバッファー読み取りまでのみ行われます。

最初のバッファー読み取りまでのみ

そのため、非常に大きなファイルの途中でNULまたはエンコードエラーが発生した場合、とにかくgrepさ​​れる可能性があります。

これはパフォーマンス上の理由によるものだと思います。

たとえば、次の行を出力します。

printf '%10000000s\n\x80a' | grep 'a'

しかし、これはしません:

printf '%10s\n\x80a' | grep 'a'

実際のバッファサイズは、ファイルの読み取り方法によって異なります。例:比較:

export LC_CTYPE='en_US.UTF-8'
(printf '\n\x80a') | grep 'a'
(printf '\n'; sleep 1; printf '\x80a') | grep 'a'

を使用するsleepと、プロセスがスリープ状態になるため1バイトしかない場合でも、最初の行はgrepに渡され、2番目の行はファイルがバイナリかどうかをチェックしません。

RTFS

git clone git://git.savannah.gnu.org/grep.git 
cd grep
git checkout v2.24

stderrエラーメッセージがエンコードされている場所を見つけます。

git grep 'Binary file'

に私達を導く/src/grep.c

if (!out_quiet && (encoding_error_output
                    || (0 <= nlines_first_null && nlines_first_null < nlines)))
    {
    printf (_("Binary file %s matches\n"), filename);

これらの変数の名前が適切であれば、基本的に結論に達しました。

encoding_error_output

クイックgrepping encoding_error_outputは、変更できる唯一のコードパスが通過することを示していますbuf_has_encoding_errors

clen = mbrlen (p, buf + size - p, &mbs);
if ((size_t) -2 <= clen)
  return true;

それからちょうどman mbrlen

nlines_first_nullおよびnlines

次のように初期化:

intmax_t nlines_first_null = -1;
nlines = 0;

そのため、nullが見つかる0 <= nlines_first_nullとtrueになります。

TODOはいつnlines_first_null < nlines偽になる可能性がありますか?怠けた。

POSIX

バイナリオプションを定義しませんgrep-パターンのファイルを検索します| pubs.opengroup.org、およびGNU grepでは文書化されていないため、RTFSが唯一の方法です。


1
印象的な説明!
ユーザー394

2
有効なUTF-8のチェックはUTF-8ロケールでのみ行われることに注意してください。また、チェックはファイルから読み取られた最初のバッファーでのみ行われ、通常のファイルではシステム上で32768バイトであるように見えますが、パイプまたはソケットでは1バイトになります。たとえば(printf '\n\0y') | grep yと比較し(printf '\n'; sleep 1; printf '\0y') | grep yてください。
ステファンシャゼラス

@StéphaneChazelas「有効なUTF-8のチェックはUTF-8ロケールでのみ行われることに注意してください」:export LC_CTYPE='en_US.UTF-8'私の例のように、または何か他のものを意味しますか?Buf read:答えに追加された素晴らしい例。あなたは明らかに私よりも多くのソースを読んで、それらのハッカーの公言「学生は啓発された」を思い出させます:-)
Ciro Santilli新疆改造中心法轮功六四事件

1
私はどちらか非常に詳細に見て、なかったが、ごく最近やった
ステファンChazelas

1
@CiroSantilli巴拿馬文件六四事件法轮功どのバージョンのGNU grepをテストしましたか?
jrw32982

6

私のテキストファイルの1つは、grepによって突然バイナリとして見られていました。

$ file foo.txt
foo.txt: ISO-8859 text

解決策は、次を使用して変換することでしたiconv

iconv -t UTF-8 -f ISO-8859-1 foo.txt > foo_new.txt

1
これは私にも起こりました。特に、原因はISO-8859-1でエンコードされた非改行スペースでした。grepでファイルを検索するには、これを通常のスペースに置き換える必要がありました。
ガレエシオ

4
grep 2.21はISO-8859テキストファイルをバイナリのように扱います。grepコマンドの前にexport LC_ALL = Cを追加します。
ネタウォーター

@netawaterありがとう!たとえば、テキストファイルにMüllerのようなものがある場合です。これは0xFC16進数であるため、grepはutf8の範囲外(最大0x7F)を期待します。printf 'a \ x7F'で確認してください| Ciroが上記のようにgrep 'a'。
アンヴァンロッサム

5

ファイル/etc/magicまたは/usr/share/misc/magicコマンドは、シーケンスのリストがあるfileファイルの種類を決定するために使用していますが。

バイナリはフォールバックソリューションにすぎないことに注意しください。時々、奇妙なエンコーディングのファイルもバイナリと見なされます。

grepLinux上のようなバイナリファイル処理するためにいくつかのオプションを持っている--binary-filesかを-U / --binary


より正確には、C99によるエンコードエラーmbrlen()。例とソースの解釈:unix.stackexchange.com/a/276028/32558
Ciro Santilli新疆改造中心法轮功六四事件

2

私の学生の一人がこの問題を抱えていました。バグがあるgrepではCygwin。ファイルに非ASCII文字が含まgrepegrepており、バイナリとして表示される場合。


これはバグではなく機能のように聞こえます。特に、それを制御するためのコマンドラインオプションがある与えられた(-a / --text)
シェパードウィル

2

「grepがファイルをバイナリと見なす理由」という質問に実際に答えると、次のように使用できますiconv

$ iconv < myfile.java
iconv: (stdin):267:70: cannot convert

私の場合、テキストエディターで正しく表示されるスペイン語の文字がありましたが、grepはそれらをバイナリと見なしました。iconv出力はそれらの文字の行番号と列番号を指し示しました

NUL文字の場合、iconvそれらを通常と見なし、そのような出力を印刷しないため、この方法は適切ではありません


1

同じ問題がありました。私は以前vi -b [filename]、追加されたキャラクターを見ていた。制御文字^@とを見つけました^M。次に、viタイプで文字:1,$s/^@//gを削除し^@ます。このコマンドを繰り返し^Mます。

警告:「青」の制御文字を押して取得するにはCtrl+ vその後、Ctrl+ MまたはCtrl+ @。次に、viを保存して終了します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.