มีวิธีที่สะดวกในการจัดประเภทไฟล์เป็น "binary" หรือ "text" หรือไม่?


35

ยูทิลิตี้ Unix มาตรฐานต้องการgrepและdiffใช้ฮิวริสติกบางอย่างเพื่อจัดประเภทไฟล์เป็น "text" หรือ "binary" ( grepเอาต์พุตของEg อาจมีบรรทัดที่เหมือนBinary file frobozz matchesกัน)

มีการทดสอบที่สะดวกที่สามารถนำไปใช้ในzshสคริปต์เพื่อดำเนินการจัดหมวดหมู่ "ข้อความ / ไบนารี" ที่คล้ายกัน? (นอกเหนือจากที่ต้องการgrep '' somefile | grep -q Binary)

(ฉันตระหนักว่าการทดสอบใด ๆ เช่นนั้นจำเป็นต้องเป็นแบบฮิวริสติกและไม่สมบูรณ์ดังนั้น)


10
fileเป็นยูทิลิตี้มาตรฐานและสามารถเรียกใช้ไฟล์มายากลเพื่อกำหนดประเภทไฟล์ให้ดีที่สุดในความสามารถของมัน มันสามารถบอกรูปแบบข้อความส่วนใหญ่และทำงานได้ค่อนข้างดีในรูปแบบไบนารี หากสิ่งที่คุณกำลังพยายามที่จะทำคือการค้นหาว่าไฟล์ที่เป็นข้อความหรือไม่ว่าเป็นคำสั่งที่คุณกำลังสนใจในการ.
Bratchley

@ แบรดลีย์: บางรุ่นfileจะพิมพ์เช่นshell scriptสำหรับไฟล์บางไฟล์ที่ฉันต้องการจัดเป็น "ข้อความ" มีวิธีที่fileจะพิมพ์textหรือbinaryไม่?
kjo

1
@don_crissti คำถามนั้นเกี่ยวกับคนที่พยายามทำให้คนบั๊กสคริปของเขา การตรวจจับข้อความเป็นสิ่งที่สคริปต์ควรทำ พวกเขาลงเอยด้วยการมีปัญหาในหนึ่งในcutคำสั่งของพวกเขา
Bratchley

1
@don_crissti ความจริงที่ว่ามีคำตอบสำหรับคำถาม A ที่ใช้งานได้สำหรับคำถาม B ไม่ได้ทำซ้ำกับ A ของ A B พิจารณาคนที่กำลังมองหาวิธีการจำแนกไฟล์เป็นข้อความหรือไบนารี มีประโยชน์อะไรบ้าง: คำถาม“ debug my script” ที่เกิดขึ้นมีคำตอบทั่วไปฝังอยู่ในคำตอบอื่น ๆ ที่เฉพาะเจาะจงกับสคริปต์นั้นหรือ“ ฉันจะจำแนก fiels เป็นข้อความหรือไบนารีได้อย่างไร?”
Gilles 'หยุดความชั่วร้าย'

1
@Gilles - ขึ้นอยู่กับว่าคุณอ่านมันอย่างไร ฉันเห็นคำถามจริง ๆ ว่าเป็นกรณีของปัญหา XY: OP ต้องการตรวจสอบว่าไฟล์เป็นไฟล์ข้อความหรือไม่และคิดว่าการfileส่งออกpiping cutเป็นวิธีแก้ปัญหา - แน่ใจว่ามีพื้นที่ที่ขาดหายไปซึ่งทำให้มันล้มเหลว คนส่วนใหญ่ที่อยู่ Y นั้นแทน X แต่ความคิดเห็นและคำตอบของStéphaneแสดงวิธีที่เหมาะสมในการพิจารณาว่าไฟล์เป็นข้อความหรือไม่
don_crissti

คำตอบ:


27

หากคุณขอfileเพียงประเภท mimeคุณจะได้รับสิ่งที่แตกต่างกันเช่นtext/x-shellscriptและapplication/x-executableอื่น ๆ แต่ฉันคิดว่าถ้าคุณเพียงตรวจสอบส่วน "ข้อความ" คุณควรได้รับผลลัพธ์ที่ดี เช่น ( -bไม่มีชื่อไฟล์ในเอาต์พุต):

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

24
เพียงจำไว้ว่าขึ้นอยู่กับคุณfileว่าคุณอาจจะพลาดรูปแบบข้อความบางส่วน: application/xml(และที่คล้ายกันเช่น RSS) application/ecmascript, application/json, image/svg+xml... คุณจะต้องรายการที่อนุญาตเหล่านั้น
Boldewyn

@Boldewyn ว้าวเป็นตัวอย่างที่ดี! ดังนั้นอาจเป็นคำตอบที่ดีกว่าก็คือยอมรับไฟล์ที่มีตัวอักษรที่พิมพ์ได้เท่านั้น แต่ก็ยังรับมือกับ utf-8 และปัญหาการเข้ารหัสที่คล้ายกัน
meuh

ใช่นั่นคือส่วนสำคัญของคำตอบของฉันด้านล่าง ปัญหาเดียวก็คือว่าวิธีการแก้ปัญหานั้นจะต้องดูไฟล์ทั้งหมด ...
Boldewyn

7
@Boldewyn ตามหลักการแล้วapplication/*ประเภทไม่ได้มีวัตถุประสงค์เพื่อการบริโภคของมนุษย์แม้ว่าพวกเขาอาจจะใช้ข้อความเพื่ออำนวยความสะดวกในการพัฒนาและการดีบัก นั่นเป็นเหตุผลที่เป็นทั้งและtext/xml application/xmlดังนั้นคำถามที่พิจารณาว่าเป็นข้อความนั้นขึ้นอยู่กับความต้องการของ OP
Tobia

3
หรือcut -d/ -f1
Stéphane Chazelas

20

อีกวิธีหนึ่งที่จะใช้isutf8จากmoreutilsคอลเลกชัน

มันออกด้วย 0 หากไฟล์นั้นถูกต้อง UTF-8 หรือ ASCII หรือลัดวงจรพิมพ์ข้อความแสดงข้อผิดพลาด (เงียบด้วย-q) และออกด้วย 1 อย่างอื่น


5
คำแนะนำที่ดี ฉันเพิ่งสังเกตเห็นว่าการให้ไดเรกทอรีตามหาเรื่องทำให้มันกลับมาเป็น 0 ฉันอยากได้ 1 อย่างน้อย แต่แล้วขยะในขยะออก
meuh

13

ถ้าคุณชอบวิธีแก้ปัญหาที่ GNU ใช้grepคุณสามารถใช้

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

มันค้นหา NUL ไบต์ในบัฟเฟอร์แรกอ่านจากไฟล์ (ไม่กี่กิโลไบต์สำหรับไฟล์ปกติ แต่อาจจะน้อยกว่ามากสำหรับไพพ์หรือซ็อกเก็ตหรืออุปกรณ์บางอย่างเช่น/dev/random) ในโลแคล UTF-8 นอกจากนี้ยังติดธงตามลำดับไบต์ที่ไม่ได้สร้างอักขระ UTF-8 ที่ถูกต้อง ถือว่าLC_ALLไม่มีการตั้งค่าเป็นภาษาที่ไม่ใช่ภาษาอังกฤษ

${1-$REPLY}รูปแบบช่วยให้คุณสามารถใช้มันเป็นzshรอบคัดเลือก glob:

ls -ld -- *(.+isbinary)

จะแสดงรายการไฟล์ไบนารี


7

คุณสามารถลองกำหนดว่าiconvสามารถอ่านไฟล์ได้หรือไม่ สิ่งนี้มีประสิทธิภาพน้อยกว่าfile(ซึ่งเพิ่งอ่านมาสองสามไบต์ตั้งแต่ต้น) แต่จะให้ผลลัพธ์ที่เชื่อถือได้มากขึ้น:

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

สิ่งนี้ทำให้iconvไม่มีการใช้งานทั่วไป แต่ถ้าพบข้อมูลที่ไม่ถูกต้อง (UTF-8 ที่ไม่ถูกต้องในตัวอย่างนี้) มันจะ barf และออก


4
การใช้-fและ-tแทนที่จะใช้ตัวเลือกแบบยาวของ GNU จะทำให้พกพาได้มากกว่า โปรดทราบว่ามันจะเรียกว่า "ไบนารี" ไฟล์ที่ไม่สามารถเปิดได้ มันจะเรียกไฟล์ "text" ที่ว่างเปล่า
Stéphane Chazelas

ตกลง iconvผมใช้รูปแบบยาวสำหรับเอกสารเฉพาะกิจสำหรับคนที่ไม่ทราบว่า แต่-fและ-tมักจะดีกว่า
Boldewyn

7

คุณสามารถเขียนสคริปต์ที่โทรfileและใช้คำสั่งกรณีและปัญหาเพื่อตรวจสอบกรณีที่คุณสนใจ

ตัวอย่างเช่น

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

แม้ว่าแน่นอนอาจมีหลายกรณีที่น่าสนใจ เพียงตรวจสอบstringsบนสำเนาของlibmagicฉันเห็นประมาณ 200 กรณีเช่น

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

บางคนใช้สตริง "ข้อความ" เป็นส่วนหนึ่งของประเภทที่แตกต่างกันเช่น

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

เช่นเดียวกันscriptอาจเป็นส่วนหนึ่งของคำ แต่ฉันไม่เห็นปัญหาในกรณีนี้ แต่สคริปต์ควรตรวจสอบ"text"ว่าเป็นคำที่ไม่ย่อย

เพื่อเป็นการเตือนความจำfileเอาต์พุตไม่ใช้คำอธิบายที่แม่นยำซึ่งมักจะมี "สคริปต์" หรือ "ข้อความ" กรณีพิเศษเป็นสิ่งที่ต้องพิจารณา ผู้ติดตามแสดงความคิดเห็นว่าการ--mime-typeทำงานในขณะที่วิธีการนี้จะไม่สำหรับ.svgไฟล์ อย่างไรก็ตามในการทดสอบฉันเห็นผลลัพธ์เหล่านี้สำหรับไฟล์ 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

ซึ่งฉันเลือกหลังจากที่เห็นไฟล์พันไฟล์แสดงเพียง 6 กับ "ข้อความ" ในผลลัพธ์ประเภท mime การจับคู่ "xml" ที่ส่วนท้ายของเอาต์พุต mime-type อาจมีประโยชน์มากกว่าพูดตรงกับ "SVG" แต่การใช้สคริปต์เพื่อทำสิ่งนั้นจะนำคุณกลับไปสู่คำแนะนำที่ทำไว้ที่นี่

ผลลัพธ์ของfileต้องมีการปรับแต่งในบางสถานการณ์และไม่น่าเชื่อถือ 100% (มันสับสนโดยสคริปต์ Perl หลายตัวของฉันเรียกพวกเขาว่า "ข้อมูล")

มีการนำไปปฏิบัติมากกว่าหนึ่งfileรายการ หนึ่งที่ใช้บ่อยที่สุดทำงานในlibmagicซึ่งสามารถใช้จากโปรแกรมที่แตกต่างกัน (อาจจะไม่โดยตรงจากzshแต่pythonสามารถ)

ตามตารางเปรียบเทียบการทดสอบไฟล์สำหรับเชลล์, Perl, Ruby และ Python , Perl มี-Tตัวเลือกที่สามารถใช้เพื่อให้ข้อมูลนี้ zshแต่มันแสดงรายการคุณลักษณะไม่มีเทียบเคียง

อ่านเพิ่มเติม:


น่าเสียดายที่fileเอาต์พุตของGNU สำหรับไฟล์ svg: SVG Scalable Vector Graphics imageไม่มีข้อความคำว่า ฉันคิดว่าวิธีการนี้จะดีกว่าคำตอบที่ยอมรับได้ในการตรวจสอบประเภท mime แต่ก็ยังขาดบางประเภท
Peter Cordes

มันยังคงพลาดท่าด้วย mime-type; สำหรับไฟล์ svg ของ xterm ฉันได้รับimage/svg+xmlแล้ว ที่จริง - เพียงตรวจสอบไฟล์ 1,000 ไฟล์เหมือนกันมีเพียง 6 ไฟล์เท่านั้นที่ออกมาเป็น "ข้อความ" ตามประเภท mime เพียงอย่างเดียว ฉันจะใช้สคริปต์ซึ่งอย่างน้อยก็สามารถทำงานได้ตามต้องการ
Thomas Dickey

3

fileมีตัวเลือก--mime-encodingที่พยายามตรวจจับการเข้ารหัสของไฟล์

 $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 binaryเพื่อตรวจสอบว่าไฟล์เป็นไฟล์ไบนารีหรือไม่ มันทำงานได้อย่างน่าเชื่อถือแม้ว่ามันจะสับสนโดยตัวอักษรที่ไม่ถูกต้องเพียงตัวเดียวในไฟล์ข้อความขนาดยาว

ตัวอย่างเช่นฉันใช้นามแฝงcatเชลล์สคริปต์ต่อไปนี้เพื่อหลีกเลี่ยงการทำลายเทอร์มินัลโดยการเปิดไฟล์ไบนารีโดยไม่ได้ตั้งใจ:

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

หมวดหมู่โดยพลการ ก่อนที่จะตอบวิธีจัดหมวดหมู่คุณต้องมีคำจำกัดความที่เข้มงวด เพื่อให้มีความหมาย, คุณต้องการวัตถุประสงค์

ดังนั้นคุณต้องการจะทำอย่างไรกับการจำแนกประเภทนี้?

  • หากคุณต้องการเลือก ascii / binary ใน FTP สิ่งสำคัญคืออย่าโอนไฟล์ไบนารีเป็น ascii (มิฉะนั้นไฟล์จะเสียหาย) ดังนั้นคุณควรทดสอบว่าไฟล์เป็นข้อความธรรมดา, html, rtf และอื่น ๆ แต่สงสัยเลือกไบนารี และคุณอาจต้องการทดสอบว่าไฟล์มีชุดย่อยเช่น 0x0A, 0x0D และ 0x20-0x7F เท่านั้น
  • หากคุณต้องการถ่ายโอนไฟล์ในบางโปรโตคอล (POP3, SMTP) คุณต้องทดสอบเพื่อเลือกว่าจะเข้ารหัสใน base64 หรือเปล่า ในกรณีนี้คุณควรทดสอบว่ามีอักขระที่ไม่รองรับหรือไม่
  • กรณีอื่น ๆ ... อาจมีคำจำกัดความอื่น ๆ

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

จะทำมัน ดูเอกสารประกอบสำหรับ-Bและ-T (ค้นหาในหน้านั้นสำหรับสตริงThe -T and -B switches work as follows)


perl -le 'print -B $ARGV[0] ? "binary" : "text"' --อาจชัดเจน หรือแม้กระทั่งperl -le 'print -B $_ ? "binary" : "text", @ARGV > 1 ? "\t$_" : "" for @ARGV' --
jrw32982 รองรับโมนิก้า

1

ฉันสนับสนุนhttps://github.com/audreyr/binaryornot มันไม่มี wrapper บรรทัดคำสั่ง (แต่) แต่นี่เป็น Python library ที่ง่ายพอที่จะโทรหาได้จาก CLI มันใช้ฮิวริสติกที่มีประสิทธิภาพพอสมควรในการพิจารณาว่าไฟล์เป็นข้อความหรือไบนารี


1

ฉันตอนนี้คำตอบนี้เก่าไปหน่อย แต่ฉันคิดว่าเพื่อนของฉันสอนให้ฉัน "แฮ็ค" ที่ดีในการทำเช่นนี้

คุณใช้diffคำสั่งและตรวจสอบไฟล์ของคุณกับไฟล์ข้อความทดสอบ:

$ diff filetocheck testfile.txt

ตอนนี้ถ้าfiletocheckเป็นไฟล์ไบนารีเอาต์พุตจะเป็น:

Binary files filetocheck and testfile.txt differ

วิธีนี้คุณสามารถใช้ประโยชน์จากdiffคำสั่งและเช่นเขียนฟังก์ชั่นที่จะตรวจสอบในสคริปต์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.