Có cách nào thuận tiện để phân loại các tập tin thành tập tin nhị phân của tập tin hay không


35

Các tiện ích Unix tiêu chuẩn thích grepdiffsử dụng một số heuristic để phân loại các tệp thành "văn bản" hoặc "nhị phân". (Ví dụ: grepđầu ra của có thể bao gồm các dòng như Binary file frobozz matches.)

Có một thử nghiệm thuận tiện nào người ta có thể áp dụng trong một zshkịch bản để thực hiện phân loại "văn bản / nhị phân" tương tự không? (Khác với những thứ như grep '' somefile | grep -q Binary.)

(Tôi nhận ra rằng bất kỳ thử nghiệm như vậy nhất thiết sẽ là heuristic, và do đó không hoàn hảo.)


10
filelà một tiện ích tiêu chuẩn và có thể chạy qua ma thuật tệp để xác định loại tệp theo khả năng tốt nhất của nó. Nó có thể cho hầu hết các định dạng văn bản và thực hiện một công việc khá tốt trên các định dạng nhị phân. Nếu tất cả những gì bạn đang cố gắng làm là tìm hiểu xem một tập tin có phải là văn bản hay không, đó là lệnh bạn quan tâm.
Bratchley

@Bratchley: một số phiên bản filesẽ in, ví dụ: shell scriptđối với một số tệp tôi muốn phân loại là "văn bản". Có cách nào để có được filein chỉ texthay binary?
kjo

1
@don_crissti Câu hỏi đó là về một người nào đó đang cố gắng khiến mọi người gỡ lỗi kịch bản bash của mình. Phát hiện văn bản chỉ là những gì kịch bản được cho là phải làm. Họ cuối cùng đã có một vấn đề trong một trong những cutmệnh lệnh của họ .
Bratchley

1
@don_crissti Thực tế là có câu trả lời cho câu hỏi A hoạt động cho câu hỏi B không phải lúc nào cũng tạo A trùng lặp với B. Hãy xem xét ai đó đang tìm cách phân loại tệp thành văn bản hoặc nhị phân. Cái nào hữu ích hơn: một câu hỏi gỡ lỗi kịch bản của tôi Câu hỏi tình cờ có câu trả lời chung chung bị chôn vùi trong số các câu trả lời khác dành riêng cho tập lệnh đó, hay một cách chung chung làm thế nào để tôi phân loại các tập tin dưới dạng văn bản hoặc nhị phân?
Gilles 'SO- ngừng trở nên xấu xa'

1
@Gilles - phụ thuộc vào cách bạn đọc nó. Tôi thực sự thấy câu hỏi đó là một trường hợp điển hình của vấn đề XY: OP ở đó muốn kiểm tra xem một tệp có phải là tệp văn bản không - và nghĩ rằng fileđầu ra đường ống cutlà giải pháp - chắc chắn, có một không gian bị thiếu khiến nó bị lỗi và điều đó đã làm hầu hết mọi người ở đó giải quyết Y thay vì X nhưng các nhận xét và câu trả lời của Stéphane cho thấy cách thức phù hợp để xác định xem tệp có phải là văn bản hay không.
don_crissti

Câu trả lời:


27

Nếu bạn yêu cầu filechỉ loại mime, bạn sẽ nhận được nhiều loại khác nhau text/x-shellscript, application/x-executablev.v., nhưng tôi tưởng tượng nếu bạn chỉ kiểm tra phần "văn bản", bạn sẽ nhận được kết quả tốt. Ví dụ: ( -bkhông có tên tệp trong đầu ra):

file -b --mime-type filename | sed 's|/.*||'

24
Chỉ cần nhớ, tùy thuộc vào bạn file, mà bạn có thể bỏ lỡ một số định dạng văn bản: application/xml(và tương tự như RSS), application/ecmascript, application/json, image/svg+xml, ... Bạn sẽ phải whitelist những.
Boldewyn

@Boldewyn wow, ví dụ hay! Vì vậy, có lẽ một câu trả lời tốt hơn là chỉ chấp nhận bất kỳ tệp nào chỉ có ký tự in được, nhưng bằng cách nào đó cũng đối phó với utf-8 và các vấn đề mã hóa tương tự.
meuh

Vâng, đó là ý chính của câu trả lời của tôi dưới đây. Vấn đề duy nhất là, giải pháp đó phải xem xét toàn bộ tập tin ...
Boldewyn

7
@Boldewyn Về nguyên tắc, application/*các loại không dành cho tiêu dùng của con người, ngay cả khi chúng có thể dựa trên văn bản để tạo điều kiện phát triển và gỡ lỗi. Đó là lý do tại sao có cả a text/xmlvà an application/xml. Vì vậy, câu hỏi có nên coi chúng là văn bản hay không phụ thuộc vào nhu cầu của OP.
Tobia

3
Hoặccut -d/ -f1
Stéphane Chazelas

20

Một cách tiếp cận khác là sử dụng isutf8từ bộ sưu tập moreutils .

Nó thoát bằng 0 nếu tệp hợp lệ UTF-8 hoặc ASCII hoặc đoản mạch, in thông báo lỗi (im lặng với -q) và thoát bằng 1 nếu không.


5
Đề nghị tốt đẹp. Tôi chỉ nhận thấy rằng việc đưa ra một thư mục như arg làm cho nó trả về 0. Tôi sẽ ít nhất thích 1. Nhưng sau đó, rác vào, rác ra.
meuh

13

Nếu bạn thích heuristic được sử dụng bởi GNU grep, bạn có thể sử dụng nó:

isbinary() {
  LC_MESSAGES=C grep -Hm1 '^' < "${1-$REPLY}" | grep -q '^Binary'
}

tìm kiếm các byte NUL trong bộ đệm đầu tiên đọc từ tệp (một vài kilo byte cho một tệp thông thường, nhưng có thể ít hơn rất nhiều cho một đường ống hoặc ổ cắm hoặc một số thiết bị như /dev/random). Trong các ngôn ngữ UTF-8, nó cũng gắn cờ trên các chuỗi byte không tạo thành các ký tự UTF-8 hợp lệ. Nó giả định LC_ALLkhông được đặt thành một cái gì đó mà ngôn ngữ không phải là tiếng Anh.

Biểu ${1-$REPLY}mẫu cho phép bạn sử dụng nó như một zshvòng loại toàn cầu:

ls -ld -- *(.+isbinary)

sẽ liệt kê các tập tin nhị phân .


7

Bạn có thể thử xác định nếu iconvcó thể đọc tệp. Điều này ít hiệu suất hơn file(chỉ đọc một vài byte từ đầu), nhưng sẽ cho bạn kết quả đáng tin cậy hơn:

ENCODING=utf-8
if iconv --from-code="$ENCODING" --to-code="$ENCODING" your_file.ext > /dev/null 2>&1; then
    echo text
else
    echo binary
fi

Điều này iconvvề cơ bản là không có op, nhưng nếu nó gặp dữ liệu không hợp lệ (UTF-8 không hợp lệ trong ví dụ này), nó sẽ barf và thoát.


4
Sử dụng -f-tthay vì các tùy chọn dài GNU sẽ làm cho nó dễ mang theo hơn. Lưu ý rằng nó sẽ gọi "nhị phân" các tệp mà nó không thể mở. Nó sẽ gọi các tập tin trống là "văn bản".
Stéphane Chazelas

Đã đồng ý. Tôi đã sử dụng các mẫu dài cho tài liệu ad hoc, cho những người không biết iconv. Nhưng -f-tthường tốt hơn.
Boldewyn

7

Bạn có thể viết một tập lệnh gọi filevà sử dụng câu lệnh tình huống để kiểm tra các trường hợp bạn quan tâm.

Ví dụ

#!/bin/sh
case $(file "$1") in
(*script*|*\ text|*\ text\ *)
    echo text
    ;;
(*)
    echo binary
    ;;
esac

mặc dù tất nhiên có thể có nhiều trường hợp đặc biệt được quan tâm. Chỉ cần kiểm tra stringsmột bản sao libmagic, tôi thấy khoảng 200 trường hợp, ví dụ,

Konqueror cookie text
Korn shell script text executable
LaTeX 2e document text
LaTeX document text
Linux Software Map entry text
Linux Software Map entry text (new format)
Linux kernel symbol map text
Lisp/Scheme program text
Lua script text executable
LyX document text
M3U playlist text
M4 macro processor script text

Một số sử dụng chuỗi "văn bản" như một phần của một loại khác, ví dụ:

SoftQuad troff Context intermediate   
SoftQuad troff Context intermediate for AT&T 495 laser printer
SoftQuad troff Context intermediate for HP LaserJet

tương tự như vậy scriptcó thể là một phần của một từ, nhưng tôi thấy không có vấn đề gì trong trường hợp này. Nhưng một kịch bản nên kiểm tra "text"dưới dạng một từ , không phải là một chuỗi con .

Xin nhắc lại, fileđầu ra không sử dụng một mô tả chính xác sẽ luôn có "tập lệnh" hoặc "văn bản". Trường hợp đặc biệt là một cái gì đó để xem xét. Một người theo dõi đã nhận xét rằng các --mime-typecông việc trong khi phương pháp này sẽ không, đối với .svgcác tệp. Tuy nhiên, trong một thử nghiệm tôi thấy các kết quả này cho các tập tin svg:

$ ls -l *.svg
-r--r--r-- 1 tom users  6679 Jul 26  2012 pumpkin_48x48.svg
-r--r--r-- 1 tom users 17372 Jul 30  2012 sink_48x48.svg
-r--r--r-- 1 tom users  5929 Jul 25  2012 vile_48x48.svg
-r--r--r-- 1 tom users  3553 Jul 28  2012 vile-mini.svg
$ file *.svg
pumpkin_48x48.svg: SVG Scalable Vector Graphics image
sink_48x48.svg:    SVG Scalable Vector Graphics image
vile-mini.svg:     SVG Scalable Vector Graphics image
vile_48x48.svg:    SVG Scalable Vector Graphics image
$ file --mime-type *.svg
pumpkin_48x48.svg: image/svg+xml
sink_48x48.svg:    image/svg+xml
vile-mini.svg:     image/svg+xml
vile_48x48.svg:    image/svg+xml

mà tôi đã chọn sau khi thấy một nghìn tệp chỉ hiển thị 6 với "văn bản" trong đầu ra kiểu mime. Có thể cho rằng, khớp "xml" ở cuối đầu ra kiểu mime có thể hữu ích hơn so với so khớp "SVG", nhưng sử dụng tập lệnh để thực hiện điều đó đưa bạn trở lại đề xuất được đưa ra ở đây.

Đầu ra của fileyêu cầu một số điều chỉnh trong một trong hai kịch bản và không đáng tin cậy 100% (nó bị nhầm lẫn bởi một số tập lệnh Perl của tôi, gọi chúng là "dữ liệu").

Có nhiều hơn một thực hiện file. Một công cụ được sử dụng phổ biến nhất thực hiện công việc của nó libmagic, có thể được sử dụng từ các chương trình khác nhau (có lẽ không trực tiếp từ zsh, mặc dù pythoncó thể).

Theo bảng so sánh kiểm tra tệp cho shell, Perl, Ruby và Python , Perl có một -Ttùy chọn mà nó có thể sử dụng để cung cấp thông tin này. Nhưng nó liệt kê không có tính năng so sánh cho zsh.

Đọc thêm:


Thật không may file, đầu ra của GNU cho các tệp svg: SVG Scalable Vector Graphics imagekhông chứa văn bản từ. Tôi nghĩ cách tiếp cận này sẽ tốt hơn câu trả lời được chấp nhận khi kiểm tra loại mime, nhưng nó vẫn bỏ lỡ một số loại.
Peter Cordes

Nó vẫn nhớ, với kiểu mime; cho tập tin svg của xterm tôi nhận được image/svg+xml. Trên thực tế - chỉ cần kiểm tra 1000 tệp giống nhau, chỉ có 6 tệp xuất hiện dưới dạng "văn bản" theo loại mime. Tôi sẽ gắn bó với một kịch bản, mà ít nhất có thể được thực hiện để làm việc khi cần thiết.
Thomas Dickey

3

filecó một tùy chọn --mime-encodingcố gắng phát hiện mã hóa của một tập tin.

 $file --mime-encoding Documents/poster2.pdf 
Documents/poster2.pdf: binary
 $file --mime-encoding projects/linux/history-torvalds/Makefile 
projects/linux/history-torvalds/Makefile: us-ascii
 $file --mime-encoding graphe.tex 
Dgraphe.tex: us-ascii
 $file --mime-encoding software.tex 
software.tex: utf-8

Bạn có thể sử dụng file --mime-encoding | grep binaryđể phát hiện nếu một tệp là một tệp nhị phân. Nó hoạt động đáng tin cậy mặc dù nó có thể bị nhầm lẫn bởi một ký tự không hợp lệ trong một tệp văn bản dài.

Ví dụ: tôi đặt bí danh catcho tập lệnh shell sau để tránh làm hỏng thiết bị đầu cuối của mình bằng cách vô tình mở tệp nhị phân:

#! /bin/sh -

[ ! -t 1 ] && exec /bin/cat "$@"
for i
do
    if file --mime-encoding -- "$i" | grep -q binary
    then
        hexdump -C -- "$i"
    else
        /bin/cat -- "$i"
    fi
done

3

Thể loại là tùy ý. Trước khi trả lời cách phân loại, bạn cần một định nghĩa (nghiêm ngặt). Để có một định nghĩa, bạn cần một mục đích .

Vì vậy, bạn muốn làm gì với phân loại đó?

  • Nếu bạn muốn chọn ascii / binary trong FTP, điều quan trọng là không chuyển tệp nhị phân là ascii (hoặc nó sẽ bị hỏng). Vì vậy, bạn kiểm tra shuld nếu tập tin là văn bản đơn giản, html, rtf và một số người khác. Nhưng trong nghi ngờ, chọn nhị phân. Và có lẽ bạn cũng muốn kiểm tra rằng tệp chỉ có một tập hợp con như 0x0A, 0x0D và 0x20-0x7F.
  • Nếu bạn muốn chuyển tệp trong một số giao thức (POP3, SMTP), bạn cần kiểm tra để chọn nếu mã hóa trong base64 hoặc chỉ đơn giản. Trong trường hợp này, bạn nên kiểm tra nếu có các ký tự không được hỗ trợ.
  • Bất kỳ trường hợp nào khác có thể có bất kỳ định nghĩa khác.

3
perl -e'chomp(my$f=<>);print "binary$/" if -B $f;print "text$/" if -T _'

sẽ làm điều đó. Xem tài liệu cho -B-T (tìm kiếm trong trang đó cho chuỗi The -T and -B switches work as follows).


perl -le 'print -B $ARGV[0] ? "binary" : "text"' --có thể rõ ràng hơn Hoặc thậm chíperl -le 'print -B $_ ? "binary" : "text", @ARGV > 1 ? "\t$_" : "" for @ARGV' --
jrw32982 hỗ trợ Monica

1

Tôi đã đóng góp cho https://github.com/audreyr/binaryornot Nó chưa có trình bao bọc dòng lệnh (nhưng) đây là một thư viện Python đơn giản đủ dễ gọi ngay cả từ CLI. Nó sử dụng một heuristic khá hiệu quả để xác định xem một tập tin là văn bản hay nhị phân.


1

Bây giờ tôi câu trả lời này hơi cũ, nhưng tôi nghĩ bạn tôi đã dạy tôi một "hack" tuyệt vời để làm điều này.

Bạn sử dụng difflệnh và kiểm tra tệp của bạn dựa vào tệp văn bản kiểm tra:

$ diff filetocheck testfile.txt

Bây giờ nếu filetochecklà một tệp nhị phân, đầu ra sẽ là:

Binary files filetocheck and testfile.txt differ

Bằng cách này, bạn có thể tận dụng difflệnh và ví dụ viết một hàm kiểm tra tập lệnh.

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.