Apa yang membuat grep menganggap file sebagai biner?


185

Saya memiliki beberapa dump database dari sistem Windows di komputer saya. Itu adalah file teks. Saya menggunakan cygwin untuk memahami mereka. Ini tampaknya file teks biasa; Saya membukanya dengan editor teks seperti notepad dan wordpad dan mereka terlihat terbaca. Namun, ketika saya menjalankan grep pada mereka, ia akan mengatakan binary file foo.txt matches.

Saya perhatikan bahwa file-file tersebut mengandung beberapa NULkarakter ascii , yang saya percaya adalah artefak dari dump database.

Jadi apa yang membuat grep menganggap file-file ini sebagai biner? The NULkarakter? Apakah ada tanda pada filesystem? Apa yang harus saya ubah untuk mendapatkan grep untuk menunjukkan kepada saya baris yang cocok?


2
--null-datasemoga bermanfaat jika NULpembatas.
Steve-o

Jawaban:


126

Jika ada NULkarakter di mana saja di dalam file, grep akan menganggapnya sebagai file biner.

Mungkin ada solusi seperti ini cat file | tr -d '\000' | yourgrepuntuk menghilangkan semua null terlebih dahulu, lalu mencari melalui file.


149
... atau gunakan -a/ --text, setidaknya dengan GNU grep.
derobert

1
@derobert: sebenarnya, pada beberapa sistem (yang lebih tua), coba lihat baris, tetapi outputnya akan memotong setiap baris yang cocok pada awalnya NUL(mungkin karena itu memanggil printf C dan memberikannya baris yang cocok?). Pada sistem seperti itu, grep cmd .sh_historyakan kembali sebanyak baris kosong karena ada baris yang cocok dengan 'cmd', karena setiap baris sh_history memiliki format tertentu dengan NULpada awal setiap baris. (tapi komentar Anda "setidaknya pada GNU grep" mungkin menjadi kenyataan. Saya tidak punya satu untuk menguji sekarang, tapi saya berharap mereka menangani ini dengan baik)
Olivier Dulac

4
Apakah kehadiran karakter NUL satu-satunya kriteria? Aku meragukan itu. Mungkin lebih pintar dari itu. Apa pun yang berada di luar kisaran Ascii 32-126 akan menjadi tebakan saya, tetapi kita harus melihat kode sumber untuk memastikan.
Michael Martinez

2
Info saya berasal dari halaman manual instance grep tertentu. Komentar Anda tentang implementasi adalah valid, sumber mengalahkan dokumen.
bbaja42

2
Saya punya file yang greppada cygwin dianggap biner karena memiliki tanda hubung panjang (0x96) dan bukan tanda hubung ASCII / minus (0x2d) biasa. Saya kira jawaban ini menyelesaikan masalah OP, tetapi tampaknya tidak lengkap.
cp.engr

121

grep -a bekerja untuk saya:

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

4
Ini adalah jawaban IMO terbaik dan paling murah.
pydsigner

Tapi tidak sesuai dengan POSIX
Matteo

21

Anda dapat menggunakan stringsutilitas untuk mengekstrak konten teks dari file apapun dan kemudian pipa melalui grep, seperti ini: strings file | grep pattern.


2
Ideal untuk mengambil file log yang mungkin rusak sebagian
Hannes R.

ya, kadang-kadang binary mixed logging juga terjadi. Ini bagus.
sdkks

13

GNU grep 2.24 RTFS

Kesimpulan: 2 dan 2 hanya kasus:

  • NULmisalnya printf 'a\0' | grep 'a'

  • kesalahan penyandian menurut C99 mbrlen(), mis:

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

    karena \x80tidak bisa menjadi byte pertama dari titik Unicode UTF-8 : UTF-8 - Keterangan | en.wikipedia.org

Selanjutnya, seperti yang disebutkan oleh Stéphane Chazelas Apa yang membuat grep menganggap file sebagai biner? | Unix & Linux Stack Exchange , pemeriksaan tersebut hanya dilakukan hingga membaca buffer pertama dengan panjang TODO.

Hanya sampai buffer pertama dibaca

Jadi, jika NUL atau kesalahan pengodean terjadi di tengah file yang sangat besar, itu mungkin akan diterima.

Saya membayangkan ini untuk alasan kinerja.

Misalnya: ini mencetak baris:

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

tetapi ini tidak:

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

Ukuran buffer aktual tergantung pada bagaimana file dibaca. Misalnya bandingkan:

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

Dengan sleep, baris pertama diteruskan ke grep walaupun panjangnya hanya 1 byte karena prosesnya tertidur, dan pembacaan kedua tidak memeriksa apakah file tersebut biner.

RTFS

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

Temukan di mana pesan kesalahan stderr dikodekan:

git grep 'Binary file'

Arahkan kami ke /src/grep.c:

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

Jika variabel-variabel tersebut dinamai dengan baik, kami pada dasarnya mencapai kesimpulan.

encoding_error_output

Grepping cepat untuk encoding_error_outputmenunjukkan bahwa satu-satunya jalur kode yang dapat memodifikasinya melewati buf_has_encoding_errors:

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

lalu saja man mbrlen.

nlines_first_null dan nlines

Diinisialisasi sebagai:

intmax_t nlines_first_null = -1;
nlines = 0;

jadi ketika null ditemukan 0 <= nlines_first_nullmenjadi true.

TODO kapan bisa nlines_first_null < nlinessalah? Saya malas.

POSIX

Tidak mendefinisikan opsi biner grep - cari file untuk suatu pola | pubs.opengroup.org , dan GNU grep tidak mendokumentasikannya, jadi RTFS adalah satu-satunya cara.


1
Penjelasan yang mengesankan!
user394

2
Perhatikan bahwa pemeriksaan untuk UTF-8 yang valid hanya terjadi di lokal UTF-8. Perhatikan juga bahwa pemeriksaan hanya dilakukan pada buffer pertama yang dibaca dari file yang untuk file biasa tampaknya 32768 byte pada sistem saya, tetapi untuk pipa atau soket bisa sekecil satu byte. Bandingkan (printf '\n\0y') | grep ydengan (printf '\n'; sleep 1; printf '\0y') | grep ymisalnya.
Stéphane Chazelas

@ StéphaneChazelas "Perhatikan bahwa cek untuk UTF-8 yang valid hanya terjadi di lokal UTF-8": maksud Anda tentang export LC_CTYPE='en_US.UTF-8'seperti pada contoh saya, atau yang lain? Tapi baca: contoh luar biasa, ditambahkan ke jawaban. Anda jelas telah membaca sumbernya lebih dari saya, mengingatkan saya pada koan peretas itu "Siswa tercerahkan" :-)
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

1
Saya juga tidak melihat ke detail yang besar, tetapi melakukannya baru
Stéphane Chazelas

1
@CiroSantilli 巴拿馬 文件 六四 事件 法轮功 versi GNU grep apa yang Anda uji?
jrw32982

6

Salah satu file teks saya tiba-tiba dilihat sebagai biner oleh grep:

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

Solusi adalah mengonversinya dengan menggunakan iconv:

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

1
Ini juga terjadi pada saya. Secara khusus, penyebabnya adalah ruang non-breaking berkode ISO-8859-1, yang harus saya ganti dengan ruang biasa untuk mendapatkan grep untuk mencari dalam file.
Gallaecio

4
grep 2.21 memperlakukan file teks ISO-8859 seolah-olah biner, tambahkan ekspor LC_ALL = C sebelum perintah grep.
netawater

@netawater Terima kasih! Ini adalah contoh kasus jika Anda memiliki sesuatu seperti Müller dalam file teks. Itu 0xFCheksadesimal, jadi di luar kisaran grep akan mengharapkan utf8 (hingga 0x7F). Periksa dengan printf 'a \ x7F' | | grep 'a' seperti yang dijelaskan Ciro di atas.
Anne van Rossum

5

File /etc/magicatau /usr/share/misc/magicmemiliki daftar urutan yang filedigunakan perintah untuk menentukan jenis file.

Perhatikan bahwa biner mungkin hanya solusi mundur. Terkadang file dengan pengodean aneh juga dianggap biner.

grepLinux memiliki beberapa opsi untuk menangani file biner seperti --binary-filesatau-U / --binary


Lebih tepatnya, encoding error menurut C99's mbrlen(). Contoh dan sumber interpretasi di: unix.stackexchange.com/a/276028/32558
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

2

Salah satu siswa saya memiliki masalah ini. Ada bug di grepdalam Cygwin. Jika file tersebut memiliki karakter non-Ascii, grepdan egrepmelihatnya sebagai biner.


Itu terdengar seperti fitur, bukan bug. Terutama mengingat ada opsi baris perintah untuk mengendalikannya (-a / --text)
Will Sheppard

2

Sebenarnya menjawab pertanyaan "Apa yang membuat grep menganggap file sebagai biner?", Anda dapat menggunakan iconv:

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

Dalam kasus saya ada karakter Spanyol yang muncul dengan benar di editor teks tetapi grep menganggapnya sebagai biner; iconvoutput menunjuk saya ke nomor baris dan kolom karakter tersebut

Dalam hal NULkarakter, iconvakan menganggapnya normal dan tidak akan mencetak output semacam itu sehingga metode ini tidak cocok


1

Saya memiliki masalah yang sama. Saya dulu vi -b [filename]melihat karakter yang ditambahkan. Saya menemukan karakter kontrol ^@dan ^M. Kemudian ketik vi :1,$s/^@//guntuk menghapus ^@karakter. Ulangi perintah ini untuk ^M.

Peringatan: Untuk mendapatkan karakter kontrol "biru" tekan Ctrl+ vlalu Ctrl+ Matau Ctrl+ @. Kemudian simpan dan keluar vi.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.