Dosyaları “ikili” veya “metin” olarak sınıflandırmanın uygun bir yolu var mı?


35

Standart Unix yardımcı programları, dosyaları "metin" veya "ikili" olarak sınıflandırmak için bazı sezgisel özellikleri kullanır grepve diffkullanır. (Örneğin, grepçıktısı çizgi gibi olabilir Binary file frobozz matches.)

zshBenzer bir "metin / ikili" sınıflandırma yapmak için komut dosyasında uygulanabilecek uygun bir test var mı ? (Gibi bir şey dışında grep '' somefile | grep -q Binary.)

(Böyle bir testin mutlaka sezgisel ve bu nedenle kusurlu olacağını biliyorum.)


10
fileStandart bir yardımcı programdır ve dosya türlerini yeteneklerinden en iyi şekilde belirlemek için dosya sihirbazını çalıştırabilir. Çoğu metin formatını söyleyebilir ve ikili formatlarda oldukça iyi bir iş çıkarır. Yapmaya çalıştığınız tek şey bir dosyanın metin olup olmadığını
bulmaksa

@Bratchley: bazı sürümleri filebasılacak, örneğin shell script, "metin" olarak sınıflandırılmasını istediğim bazı dosyalar için. Almanın bir yolu var mı filesadece yazdırmak textveya binary?
kjo

1
@ don_crissti Bu soru, insanların bash betiğini ayıklamalarını sağlamaya çalışan biri hakkında. Metni tespit etmek, sadece betiğin yapması gereken şeydir. cutKomutlarından birinde sorun yaşadılar.
Bratchley

1
@ don_crissti A sorusu için B sorusu için çalışan bir cevabın olması her zaman A'yı B'nin kopyası yapmaz. Dosyaları metin ya da ikili olarak sınıflandırmanın bir yolunu arayan birini düşünün. Hangisi daha kullanışlıdır: o betiğe özgü diğer cevapların arasına jenerik bir cevaba sahip olan bir “betiğimi hata ayıkla” sorusu ya da “fielleri metin veya ikili olarak nasıl sınıflandırabilirim?” Sorusu nedir?
Gilles 'SO- kötü olmayı bırak'

1
@Gilles - nasıl okuduğunuza bağlıdır. Aslında soruyu tipik bir XY problemi vakası olarak görüyorum: OP, bir dosyanın bir metin dosyası olup olmadığını kontrol etmek istiyor - ve boru fileçıktısının cutçözüm olduğunu düşünüyor - elbette, başarısız olmasına neden olan eksik bir alan var. Oradaki çoğu kişi X yerine Y'yi ele alır, ancak Stéphane'nin yorumları ve yanıtı dosyanın metin olup olmadığını belirlemenin doğru yolunu gösterir.
don_crissti

Yanıtlar:


27

fileSadece mime türünü sorarsanız text/x-shellscript, application/x-executablevb. Gibi pek çok farklı olanı alırsınız , ancak "metin" bölümünü kontrol ederseniz iyi sonuçlar almanız gerektiğini hayal ediyorum. Örneğin ( -bçıktıda dosya adı yok):

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

24
Sadece senin bağlı olarak hatırlamıyorum filebazı metin biçimleri kaçırabilir ki: application/xml(ve RSS gibi benzeri), application/ecmascript, application/json, image/svg+xml, ... Sen beyaz liste için bu olurdu.
Boldewyn

@Boldewyn vay, güzel örnekler! Bu yüzden muhtemelen daha iyi bir cevap sadece yazdırılabilir karakterleri olan herhangi bir dosyayı kabul etmek, fakat bir şekilde utf-8 ve benzer kodlama problemleriyle başa çıkmaktır.
meuh

Evet, aşağıdaki cevabımın özü bu. Tek sorun, bu çözümün bütün dosyaya
bakması gerektiğidir

7
@Boldewyn Prensip olarak, application/*geliştirme ve hata ayıklamayı kolaylaştırmak için metin tabanlı olsalar bile , türler insan tüketimine yönelik değildir. Bir orada hem olmasının nedeni de budur text/xmlve bir application/xml. Bu yüzden onları metin olarak kabul edip etmeme sorusu OP'nin ihtiyaçlarına bağlıdır.
Tobia,

3
Veyacut -d/ -f1
Stéphane Chazelas

20

Bir başka yaklaşım isutf8, moreutils koleksiyonundan kullanmak olacaktır .

Dosya UTF-8 veya ASCII geçerliyse veya kısa devreler varsa, bir hata mesajı basarsa (sessizle -q) ve 0 ile çıkarsa 0 ile çıkar.


5
Güzel öneri. Bir dizini arg olarak vermenin 0 döndürdüğünü fark ettim. En azından 1 tanesini tercih ederdim. Ama sonra, çöp içeri, çöp dışarı.
meuh

13

GNU tarafından kullanılan sezgisel bulmayı grepseviyorsanız, kullanabilirsiniz:

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

Bu NUL ilk tampon dosyadan okunan bayt arar (normal dosya için birkaç kilo-bayt, ama çok daha az bir boru veya soket ya da bazı cihazlar gibi için olabilir /dev/random). UTF-8 yerellerinde, geçerli UTF-8 karakterleri oluşturmayan bayt dizilerine de işaretler. LC_ALLDilin İngilizce olmadığı bir şeye ayarlanmadığı varsayılmaktadır .

${1-$REPLY}Form, bir olarak kullanmak için izin verir zshglob eleme:

ls -ld -- *(.+isbinary)

İkili dosyaları listelerdi .


7

iconvDosyayı okuyabilir mi karar vermeyi deneyebilirsiniz . Bu, file(başlangıçtan sadece birkaç bayt okur) ' dan daha az performans gösterir , ancak size daha güvenilir sonuçlar verir:

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

Bu iconvtemelde no-op yapar , ancak geçersiz verilerle karşılaşırsa (bu örnekte geçersiz UTF-8), korkar ve çıkar.


4
GNU uzun seçeneklerinin yerine -fve -tbunun kullanılması daha taşınabilir hale getirecektir. Açılamadığı dosyaları "ikili" olarak çağırır. Boş dosyaları "text" olarak çağırır.
Stéphane Chazelas

Kabul. Uzun formları özel belgeler için, bilmeyenler için kullandım iconv. Ancak -fve -tgenellikle daha iyidir.
Boldewyn

7

Çağrılan bir komut dosyası yazabilir fileve ilgilendiğiniz davaları kontrol etmek için bir vaka ifadesi kullanabilirsiniz.

Örneğin

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

Tabii ki ilgilenilen birçok özel durum olabilir. Sadece stringsbir kopyasını kontrol libmagicediyorum, yaklaşık 200 vaka görüyorum, örneğin,

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

Bazıları "text" dizesini farklı bir türün parçası olarak kullanır;

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

Aynı şekilde scriptbir kelimenin parçası olabilir, ama bu durumda hiçbir sorun görmüyorum. Ancak bir script "text"bir kelime olarak değil, bir alt kelimeyi kontrol etmelidir .

Hatırlatma olarak, fileçıktı her zaman "script" veya "text" içerecek kesin bir açıklama kullanmaz. Özel durumlar, dikkate alınması gereken bir şeydir. Bir takip --mime-type, .svgdosyalar için bu yaklaşımın işe yaramayacağını belirtti . Bununla birlikte, bir testte svg dosyaları için şu sonuçları görüyorum:

$ 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

Binlerce dosyayı gördükten sonra seçtiğim mime tipi çıktıda sadece "text" yazısını gösteriyor. Muhtemelen, mime tipi çıktının sonunda "xml" yi eşleştirmek, "SVG" yi eşleştirmek yerine daha yararlı olabilir, ancak bunu yapmak için bir komut dosyası kullanmak , sizi burada yapılan öneriye geri götürür.

Her fileiki senaryoda da bazı ayarlamalar yapılmasını gerektirir ve% 100 güvenilir değildir (bazı Perl komut dosyalarım tarafından karıştırılır, bunları "veri" olarak adlandırır).

Birden fazla uygulaması var file. En sık kullanılanı, libmagicfarklı programlardan (belki de doğrudan zsholsa pythonda değil) kullanılabilecek çalışmalarını yapar.

Göre kabuk, Perl, Ruby ve Python için dosya testi karşılaştırma tablosu , Perl bir vardır -Tbu bilgileri sağlamak için kullanabilir seçeneği. Ancak bunun için karşılaştırılabilir bir özellik yok zsh.

Daha fazla okuma:


Ne yazık ki GNU file'nun svg dosyaları için çıktısı: SVG Scalable Vector Graphics imagemetin sözcüğü içermiyor. Bu yaklaşımın mime tipini kontrol etmenin kabul edilen cevabından daha iyi olacağını düşünmüştüm, ama yine de bazı tipleri özlüyor.
Peter Cordes

MIME türü ile hala özlüyor; xterm'in svg dosyası için image/svg+xml. Aslında - sadece 1000 dosyalık bir dosyayı kontrol ettim, sadece 6 dilime göre sadece yazı tipine göre "metin" çıktı. En azından gerektiği gibi çalışmak için yapılabilecek bir senaryoya bağlı kalacağım.
Thomas Dickey,

3

file--mime-encodingbir dosyanın kodlamasını algılamaya çalışan bir seçeneğe sahiptir .

 $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

file --mime-encoding | grep binaryBir dosyanın ikili bir dosya olup olmadığını tespit etmek için kullanabilirsiniz . Uzun bir metin dosyasında tek bir geçersiz karakter ile karıştırılsa da güvenilir bir şekilde çalışır.

Örneğin, catyanlışlıkla bir ikili dosyayı açarak terminalimi mahvetmemek için aşağıdaki kabuk betiğine takılıyorum:

#! /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

Kategoriler keyfidir. Nasıl bir sınıflandırma yapılacağı cevabından önce, (kesin) bir tanımlamaya ihtiyacınız vardır. Bir tanımın olması için bir amaca ihtiyacın var .

Peki, bu sınıflandırma ile ne yapmak istiyorsunuz?

  • FTP'de ascii / binary seçmek istiyorsanız, bir ikili dosyayı ascii olarak aktarmamanız önemlidir (veya bozuk olacaktır). Bu nedenle, dosyanın düz metinler, html, rtf ve diğerleri olup olmadığını sınamalısınız. Ancak şüphede, ikiliyi seçin. Ve belki de dosyanın yalnızca 0x0A, 0x0D ve 0x20-0x7F gibi bir altküme sahip olduğunu test etmek istersiniz.
  • Dosyayı bazı protokollerde (POP3, SMTP) aktarmak istiyorsanız, base64'te mi yoksa sadece düz olarak mı kodlanacağını seçmek için test etmeniz gerekir. Bu durumda, desteklenmeyen karakterler olup olmadığını test etmelisiniz.
  • Başka bir durumda… Başka bir tanımı olabilir.

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

yapacağım. Bkz belgelerine -Bve-T (dize için o sayfa içinde arama The -T and -B switches work as follows).


perl -le 'print -B $ARGV[0] ? "binary" : "text"' --daha net olabilir. Veya hattaperl -le 'print -B $_ ? "binary" : "text", @ARGV > 1 ? "\t$_" : "" for @ARGV' --
jrw32982,

1

Https://github.com/audreyr/binaryornot'a katkıda bulundum. Komut satırı sarmalayıcısına sahip değil (henüz) ancak bu, CLI'den bile çağırması kolay, basit bir Python kütüphanesi. Bir dosyanın metin mi, yoksa ikili mi olduğunu belirlemek için oldukça etkili bir sezgisel tarama kullanır.


1

Şimdi bu cevap biraz eski, ama sanırım arkadaşım bana bunu yapmak için harika bir "kes" öğretti.

diffKomutu kullanır ve dosyanızı bir test metni dosyasına karşı kontrol eder:

$ diff filetocheck testfile.txt

Şimdi filetocheckbir ikili dosya ise, çıktı şöyle olur:

Binary files filetocheck and testfile.txt differ

Bu yolla diffkomuttan faydalanabilir ve örneğin bir komut dosyasında kontrolü yapan bir fonksiyon yazabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.