Điều gì làm cho grep coi một tập tin là nhị phân?


185

Tôi có một số cơ sở dữ liệu từ hệ thống Windows trên hộp của tôi. Chúng là các tệp văn bản. Tôi đang sử dụng cygwin để grep qua chúng. Chúng dường như là các tệp văn bản đơn giản; Tôi mở chúng bằng các trình soạn thảo văn bản như notepad và wordpad và chúng trông dễ đọc. Tuy nhiên, khi tôi chạy grep trên chúng, nó sẽ báo binary file foo.txt matches.

Tôi đã nhận thấy rằng các tệp chứa một số NULký tự ascii , mà tôi tin là các tạo phẩm từ kết xuất cơ sở dữ liệu.

Vậy điều gì khiến grep coi những tập tin này là nhị phân? Nhân NULvật nào? Có một cờ trên hệ thống tập tin? Tôi cần thay đổi gì để có được grep để hiển thị cho tôi các kết quả khớp dòng?


2
--null-datacó thể hữu ích nếu NULlà dấu phân cách
Steve-o

Câu trả lời:


125

Nếu có một NULký tự ở bất cứ đâu trong tệp, grep sẽ coi đó là tệp nhị phân.

Có thể có một cách giải quyết như thế này cat file | tr -d '\000' | yourgrepđể loại bỏ tất cả null trước, sau đó tìm kiếm qua tệp.


149
... hoặc sử dụng -a/ --text, ít nhất là với GNU grep.
derobert

1
@derobert: trên thực tế, trên một số hệ thống (cũ hơn), grep thấy các dòng, nhưng đầu ra của nó sẽ cắt từng dòng phù hợp ở đầu tiên NUL(có thể vì nó gọi là printf của C và cho nó là dòng phù hợp?). Trên một hệ thống như vậy, a grep cmd .sh_historysẽ trả về nhiều dòng trống như có các dòng khớp với 'cmd', vì mỗi dòng của sh_history có một định dạng cụ thể với một NULbắt đầu của mỗi dòng. (nhưng nhận xét của bạn "ít nhất là trên GNU grep" có thể trở thành sự thật. Tôi không có sẵn trong tay để kiểm tra, nhưng tôi hy vọng họ xử lý việc này tốt đẹp)
Olivier Dulac

4
Là sự hiện diện của một nhân vật NUL là tiêu chí duy nhất? Tôi nghi ngờ điều đó. Có lẽ nó thông minh hơn thế. Bất cứ điều gì nằm ngoài phạm vi 32-126 của Ascii sẽ là phỏng đoán của tôi, nhưng chúng ta phải xem mã nguồn để chắc chắn.
Michael Martinez

2
Thông tin của tôi là từ trang man của ví dụ grep cụ thể. Nhận xét của bạn về việc thực hiện là hợp lệ, nguồn tài liệu hơn hẳn.
bbaja42

2
Tôi đã có một tệp mà greptrên cygwin coi là nhị phân vì nó có dấu gạch ngang dài (0x96) thay vì dấu gạch nối / dấu ASCII thông thường (0x2d). Tôi đoán câu trả lời này đã giải quyết vấn đề của OP, nhưng có vẻ như nó chưa hoàn chỉnh.
cp.engr

121

grep -a đã làm cho tôi:

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

4
Đây là câu trả lời tốt nhất, ít tốn kém nhất IMO.
pydsigner

Nhưng không tuân thủ POSIX
Matteo

21

Bạn có thể sử dụng stringstiện ích để trích xuất nội dung văn bản từ bất kỳ tệp nào và sau đó chuyển nó qua grep, như thế này : strings file | grep pattern.


2
Lý tưởng để lấy các tệp nhật ký có thể bị hỏng một phần
Hannes R.

có, đôi khi đăng nhập hỗn hợp nhị phân cũng xảy ra. Điều này là tốt
sdkks

13

GNU grep 2.24 RTFS

Kết luận: chỉ 2 và 2 trường hợp:

  • NUL, ví dụ printf 'a\0' | grep 'a'

  • lỗi mã hóa theo C99 mbrlen(), ví dụ:

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

    bởi vì \x80không thể là byte đầu tiên của điểm Unicode UTF-8: UTF-8 - Mô tả | vi.wikipedia.org

Hơn nữa, như được đề cập bởi Stéphane Chazelas Điều gì khiến grep coi một tệp là nhị phân? | Unix & Linux Stack Exchange , những kiểm tra đó chỉ được thực hiện cho đến lần đọc bộ đệm đầu tiên có độ dài TODO.

Chỉ đọc đến bộ đệm đầu tiên

Vì vậy, nếu một lỗi NUL hoặc mã hóa xảy ra ở giữa một tệp rất lớn, thì nó có thể bị lỗi.

Tôi tưởng tượng điều này là vì lý do hiệu suất.

Ví dụ: cái này in dòng:

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

nhưng điều này không:

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

Kích thước bộ đệm thực tế phụ thuộc vào cách đọc tệp. Ví dụ: so sánh:

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

Với sleep, dòng đầu tiên được chuyển đến grep ngay cả khi nó chỉ dài 1 byte vì quá trình chuyển sang chế độ ngủ và lần đọc thứ hai không kiểm tra xem tệp có phải là nhị phân hay không.

RTFS

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

Tìm nơi thông báo lỗi stderr được mã hóa:

git grep 'Binary file'

Dẫn chúng tôi đến /src/grep.c:

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

Nếu các biến đó được đặt tên tốt, về cơ bản chúng tôi đã đi đến kết luận.

mã hóa_errorDefput

Grepping nhanh cho encoding_error_outputthấy rằng đường dẫn mã duy nhất có thể sửa đổi nó đi qua buf_has_encoding_errors:

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

sau đó chỉ cần man mbrlen.

nlines_first_null và nlines

Khởi tạo là:

intmax_t nlines_first_null = -1;
nlines = 0;

vì vậy khi một null được tìm thấy 0 <= nlines_first_nulltrở thành sự thật.

TODO nlines_first_null < nlinesbao giờ có thể sai? Tôi đã lười biếng.

MỘT BỘ MÔ TẢ CHÍNH THỨC CUNG CẤP MỘT TIÊU CHUẨN CHO THIẾT KẾ HỆ ĐIỀU HÀNH, ĐẶC BIỆT LÀ CÁC MÔ TẢ TƯƠNG THÍCH VỚI UNIX

Không xác định tùy chọn nhị phân grep - tìm kiếm tệp cho mẫu | pubs.opengroup.org và GNU grep không ghi lại nó, vì vậy RTFS là cách duy nhất.


1
Giải thích ấn tượng!
dùng394

2
Lưu ý rằng việc kiểm tra UTF-8 hợp lệ chỉ xảy ra ở các địa điểm UTF-8. Cũng lưu ý rằng việc kiểm tra chỉ được thực hiện trên bộ đệm đầu tiên được đọc từ tệp mà đối với một tệp thông thường dường như là 32768 byte trên hệ thống của tôi, nhưng đối với một đường ống hoặc ổ cắm có thể nhỏ bằng một byte. So sánh (printf '\n\0y') | grep yvới (printf '\n'; sleep 1; printf '\0y') | grep yví dụ.
Stéphane Chazelas

@ StéphaneChazelas "Lưu ý rằng việc kiểm tra UTF-8 hợp lệ chỉ xảy ra ở các địa điểm UTF-8": bạn có ý nghĩa export LC_CTYPE='en_US.UTF-8'như trong ví dụ của tôi hay điều gì khác không? Buf đọc: ví dụ tuyệt vời, thêm vào để trả lời. Bạn đã rõ ràng là đọc các nguồn hơn tôi, làm tôi nhớ đến những công án của hacker "Học sinh đã giác ngộ" :-)
Ciro Santilli新疆改造中心法轮功六四事件

1
Tôi cũng không nhìn vào chi tiết tuyệt vời, nhưng đã làm rất gần đây
Stéphane Chazelas

1
@CiroSantilli 巴拿馬 件 version bạn đã thử nghiệm phiên bản GNU grep nào?
jrw32982

6

Một trong những tệp văn bản của tôi đột nhiên bị grep xem là nhị phân:

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

Giải pháp là chuyển đổi nó bằng cách sử dụng iconv:

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

1
Điều này cũng xảy ra với tôi. Cụ thể, nguyên nhân là một không gian không phá vỡ được mã hóa theo ISO-8859-1, mà tôi phải thay thế bằng một không gian thông thường để có được grep để tìm kiếm trong tệp.
Gallaecio

4
grep 2.21 xử lý các tệp văn bản ISO-8859 như thể chúng là nhị phân, thêm export LC_ALL = C trước lệnh grep.
netawater

@netawater Cảm ơn! Đây là ví dụ nếu bạn có một cái gì đó giống như Müller trong một tệp văn bản. Đó là 0xFChệ thập lục phân, do đó, ngoài phạm vi grep sẽ mong đợi cho utf8 (tối đa 0x7F). Kiểm tra với printf 'a \ x7F' | grep 'a' như Ciro mô tả ở trên.
Anne van Rossum

5

Tệp /etc/magichoặc /usr/share/misc/magiccó một danh sách các chuỗi mà lệnh filesử dụng để xác định loại tệp.

Lưu ý rằng nhị phân có thể chỉ là một giải pháp dự phòng. Đôi khi các tập tin với mã hóa lạ cũng được coi là nhị phân.

greptrên Linux có một số tùy chọn để xử lý các tệp nhị phân như --binary-fileshoặc-U / --binary


Chính xác hơn, lỗi mã hóa theo C99 mbrlen(). Ví dụ và giải thích nguồn tại: unix.stackexchange.com/a/276028/32558
Ciro Santilli

2

Một trong những học sinh của tôi đã có vấn đề này. Có một lỗi greptrong Cygwin. Nếu tệp có các ký tự không phải là chữ Asii grepegrepxem nó là nhị phân.


Nghe có vẻ như một tính năng, không phải là một lỗi. Đặc biệt là có một tùy chọn dòng lệnh để điều khiển nó (-a / --text)
Will Sheppard

2

Trên thực tế, trả lời câu hỏi "Điều gì khiến grep coi một tệp là nhị phân?", Bạn có thể sử dụng iconv:

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

Trong trường hợp của tôi, có các ký tự tiếng Tây Ban Nha xuất hiện chính xác trong các trình soạn thảo văn bản nhưng grep coi chúng là nhị phân; iconvđầu ra chỉ cho tôi số dòng và số cột của các ký tự đó

Trong trường hợp các NULký tự, iconvsẽ coi chúng là bình thường và sẽ không in loại đầu ra đó vì vậy phương pháp này không phù hợp


1

Tôi đã từng gặp vấn đề tương tự. Tôi đã từng vi -b [filename]thấy các nhân vật được thêm vào. Tôi tìm thấy các nhân vật điều khiển ^@^M. Sau đó trong vi loại :1,$s/^@//gđể loại bỏ các ^@ký tự. Lặp lại lệnh này cho ^M.

Cảnh báo: Để có được các ký tự điều khiển "màu xanh", nhấn Ctrl+ vrồi Ctrl+ Mhoặc Ctrl+ @. Sau đó lưu và thoát vi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.