grep ทำให้อะไรที่พิจารณาว่าเป็นไฟล์ไบนารี่?


185

ฉันมีฐานข้อมูลบางส่วนจากระบบ Windows บนกล่องของฉัน พวกเขาเป็นไฟล์ข้อความ ฉันใช้ cygwin เพื่อ grep ผ่านพวกเขา ดูเหมือนว่าจะเป็นไฟล์ข้อความธรรมดา ฉันเปิดพวกมันด้วยโปรแกรมแก้ไขข้อความเช่น notepad และ wordpad และพวกมันดูเข้าใจง่าย แต่เมื่อผมทำงาน grep binary file foo.txt matchesกับพวกเขาก็จะบอกว่า

ฉันสังเกตว่าไฟล์มีNULอักขระASCII บางตัวซึ่งฉันเชื่อว่าเป็นสิ่งประดิษฐ์จากดัมพ์ของฐานข้อมูล

ดังนั้นอะไรทำให้ grep พิจารณาว่าไฟล์เหล่านี้เป็นไบนารี NULตัวละคร? มีการตั้งค่าสถานะบนระบบแฟ้มหรือไม่? ฉันต้องเปลี่ยนอะไรเพื่อให้ grep แสดงบรรทัดที่ตรงกัน


2
--null-dataอาจมีประโยชน์หากNULเป็นตัวคั่น
Steve-o

คำตอบ:


125

หากมีNULอักขระใด ๆ ในไฟล์ grep จะพิจารณาว่าเป็นไฟล์ไบนารี

อาจมีวิธีแก้ปัญหาเช่นนี้cat file | tr -d '\000' | yourgrepเพื่อกำจัด null ทั้งหมดก่อนจากนั้นค้นหาไฟล์


149
... หรือใช้-a/ --textอย่างน้อยกับ grep GNU
Derobert

1
@derobert: จริง ๆ แล้วในบางระบบ (เก่ากว่า) grep ดูบรรทัด แต่ผลลัพธ์จะตัดปลายแต่ละบรรทัดที่ตรงกันในตอนแรกNUL(อาจเป็นเพราะมันเรียกว่า printf ของ C และให้บรรทัดที่ตรงกันหรือไม่ ในระบบดังกล่าว a grep cmd .sh_historyจะส่งคืนบรรทัดว่างเปล่าจำนวนมากเนื่องจากมีการจับคู่บรรทัด 'cmd' เนื่องจากแต่ละบรรทัดของ sh_history มีรูปแบบเฉพาะโดยมีNULจุดเริ่มต้นของแต่ละบรรทัด ( แต่ความคิดเห็นของคุณ "อย่างน้อยใน GNU grep" อาจจะเป็นจริงผมไม่ได้มีอย่างใดอย่างหนึ่งที่อยู่ในมือตอนนี้เพื่อทดสอบ แต่ผมคาดว่าพวกเขาจะจัดการกับปัญหานี้เป็นอย่างดี.)
โอลิเวีย Dulac

4
การมีอักขระ NUL เป็นเพียงเกณฑ์หรือไม่? ฉันสงสัยมัน. มันอาจฉลาดกว่านั้น อะไรก็ตามที่อยู่นอกช่วง Ascii 32-126 นั้นน่าจะเป็นการคาดเดาของฉัน แต่เราต้องดูซอร์สโค้ดอย่างแน่นอน
Michael Martinez

2
ข้อมูลของฉันมาจาก man page ของ grep instance ที่เจาะจง ความคิดเห็นของคุณเกี่ยวกับการใช้งานถูกต้องแหล่งเอกสารสำคัญกว่า
bbaja42

2
ฉันมีไฟล์ที่grepcygwin พิจารณาเป็นเลขฐานสองเพราะมันมีเส้นประยาว (0x96) แทนที่จะเป็นเครื่องหมายขีดคั่น ASCII ปกติ / ลบ (0x2d) ฉันเดาคำตอบนี้แก้ปัญหาของ OP แต่ดูเหมือนว่ามันไม่สมบูรณ์
cp.engr

121

grep -a ทำงานให้ฉัน:

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

4
นี่คือคำตอบที่ดีที่สุดและราคาถูกที่สุด IMO
pydsigner

แต่ไม่เป็นไปตาม POSIX
Matteo

21

คุณสามารถใช้stringsยูทิลิตี้ที่จะดึงเนื้อหาข้อความจากไฟล์แล้วท่อใด ๆ มันผ่านเช่นนี้grepstrings file | grep pattern


2
เหมาะอย่างยิ่งสำหรับ grepping ไฟล์บันทึกที่อาจเสียหายบางส่วน
Hannes R.

ใช่บางครั้งการบันทึกผสมแบบไบนารี่ก็เกิดขึ้นเช่นกัน ดีจัง.
sdkks

13

GNU grep 2.24 RTFS

สรุป: 2 และ 2 รายเท่านั้น:

  • NUL, เช่น printf 'a\0' | grep 'a'

  • ข้อผิดพลาดในการเข้ารหัสตาม C99 mbrlen()เช่น:

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

    เนื่องจาก\x80ไม่สามารถเป็นไบต์แรกของจุด Unicode UTF-8 : UTF-8 - คำอธิบาย | en.wikipedia.org

นอกจากนี้ตามที่กล่าวโดยStéphane Chazelas grep ทำให้อะไรที่พิจารณาว่าไฟล์เป็น binary? | Unix & Linux Stack Exchange การตรวจสอบเหล่านั้นทำได้จนถึงการอ่านบัฟเฟอร์ครั้งแรกที่มีความยาวสิ่งที่ต้องทำ

อ่านบัฟเฟอร์แรกสุดเท่านั้น

ดังนั้นหากมีข้อผิดพลาด NUL หรือการเข้ารหัสเกิดขึ้นกลางไฟล์ที่มีขนาดใหญ่มากมันอาจเกิดความผิดพลาดขึ้นได้

ฉันคิดว่านี่เป็นเหตุผลด้านประสิทธิภาพ

เช่น: พิมพ์บรรทัดนี้:

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

แต่นี่ไม่ได้:

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

ขนาดบัฟเฟอร์ที่แท้จริงขึ้นอยู่กับการอ่านไฟล์ เช่นเปรียบเทียบ:

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

ด้วยsleep, บรรทัดแรกจะถูกส่งผ่านไปยัง grep แม้ว่าจะมีความยาวเพียง 1 ไบต์เนื่องจากกระบวนการเข้าสู่โหมดสลีปและการอ่านครั้งที่สองไม่ได้ตรวจสอบว่าไฟล์เป็นไบนารีหรือไม่

RTFS

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

ค้นหาตำแหน่งที่มีการเข้ารหัสข้อความแสดงข้อผิดพลาด:

git grep 'Binary file'

นำเราไปที่/src/grep.c:

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

หากตัวแปรเหล่านั้นถูกตั้งชื่ออย่างดีเราก็มาถึงข้อสรุป

encoding_error_output

การ grepping อย่างรวดเร็วเพื่อencoding_error_outputแสดงให้เห็นว่ามีเพียงเส้นทางรหัสที่สามารถแก้ไขได้buf_has_encoding_errors:

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

เพียงแค่man mbrlenนั้น

nlines_first_null และ nlines

เริ่มต้นเป็น:

intmax_t nlines_first_null = -1;
nlines = 0;

ดังนั้นเมื่อพบค่าว่างจะ0 <= nlines_first_nullกลายเป็นจริง

สิ่งที่ต้องทำเมื่อnlines_first_null < nlinesเคยเป็นเท็จ? ฉันขี้เกียจ

POSIX

ไม่ได้กำหนดgrep options ของไบนารี- ค้นหาไฟล์เพื่อหา pattern | pubs.opengroup.orgและ grep GNU ไม่ได้จัดทำเอกสารดังนั้น RTFS จึงเป็นวิธีเดียวเท่านั้น


1
คำอธิบายที่น่าประทับใจ!
user394

2
โปรดทราบว่าการตรวจสอบ UTF-8 ที่ถูกต้องจะเกิดขึ้นในตำแหน่งที่ตั้ง UTF-8 เท่านั้น โปรดทราบด้วยว่าการตรวจสอบจะทำเฉพาะในบัฟเฟอร์แรกที่อ่านจากไฟล์ซึ่งสำหรับไฟล์ปกติดูเหมือนว่าจะมีขนาด 32768 ไบต์ในระบบของฉัน แต่สำหรับไพพ์หรือซ็อกเก็ตอาจมีขนาดเล็กเท่ากับหนึ่งไบต์ เปรียบเทียบ(printf '\n\0y') | grep yกับ(printf '\n'; sleep 1; printf '\0y') | grep yตัวอย่าง
Stéphane Chazelas

@ StéphaneChazelas "โปรดทราบว่าการตรวจสอบ UTF-8 ที่ถูกต้องจะเกิดขึ้นในตำแหน่งที่ตั้ง UTF-8 เท่านั้น": คุณหมายถึงสิ่งexport LC_CTYPE='en_US.UTF-8'ที่อยู่ในตัวอย่างหรืออย่างอื่นหรือไม่? Buf read: ตัวอย่างที่น่าอัศจรรย์เพิ่มเข้ามาเพื่อตอบ เห็นได้ชัดว่าคุณอ่านที่มามากกว่าฉันเตือนฉันของแฮ็กเกอร์ koans "นักเรียนได้รับการตรัสรู้" :-)
Ciro Santilli 新疆改造中心中心法轮功六四事件

1
ผมไม่ได้มองเข้าไปในรายละเอียดมากทั้ง แต่ไม่มากเมื่อเร็ว ๆ นี้
Stéphane Chazelas

1
@CiroSantilli gre 文件六四事件法轮功 grep GNU รุ่นไหนที่คุณทดสอบ?
jrw32982

6

หนึ่งในไฟล์ข้อความของฉันถูกจู่ ๆ ถูกมองว่าเป็นเลขฐานสองโดย grep:

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

ทางออกคือการแปลงโดยใช้iconv:

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

1
เรื่องนี้เกิดขึ้นกับฉันเช่นกัน โดยเฉพาะสาเหตุคือ ISO-8859-1 ที่ไม่ทำลายพื้นที่ซึ่งฉันต้องแทนที่ด้วยช่องว่างปกติเพื่อรับ grep เพื่อค้นหาในไฟล์
Gallaecio

4
grep 2.21 ปฏิบัติกับไฟล์ข้อความ ISO-8859 ราวกับว่าเป็นไบนารีให้เพิ่มการส่งออก LC_ALL = C ก่อนคำสั่ง grep
netawater

@netawater ขอบคุณ! นี่เป็นกรณีตัวอย่างถ้าคุณมีบางอย่างเช่นMüllerในไฟล์ข้อความ นั่นคือ0xFCเลขฐานสิบหกดังนั้นนอกช่วง grep จะคาดหวังสำหรับ utf8 (มากถึง0x7F) ตรวจสอบกับ printf 'a \ x7F' | grep 'a' ดังที่ Ciro อธิบายไว้ข้างต้น
Anne van Rossum

5

ไฟล์/etc/magicหรือ/usr/share/misc/magicมีรายการลำดับที่คำสั่งfileใช้เพื่อกำหนดประเภทไฟล์

โปรดทราบว่าไบนารีอาจเป็นโซลูชันสำรอง บางครั้งไฟล์ที่มีการเข้ารหัสที่แปลกก็ถือเป็นไบนารีด้วย

grepบน Linux มีตัวเลือกบางอย่างเพื่อจัดการไฟล์ไบนารีเช่น--binary-filesหรือ-U / --binary


อีกอย่างแม่นยำเข้ารหัสข้อผิดพลาดตาม mbrlen()C99 ตัวอย่างและการตีความแหล่งที่มาที่: unix.stackexchange.com/a/276028/32558
Ciro Santilli 事件改造中心中心法轮功六四事件

2

นักเรียนคนหนึ่งของฉันมีปัญหานี้ มีข้อบกพร่องgrepในCygwinระบบ หากไฟล์มีอักขระที่ไม่ใช่ ASCII grepและegrepดูว่าเป็นไบนารี


ฟังดูเหมือนฟีเจอร์ไม่ใช่ข้อผิดพลาด โดยเฉพาะอย่างยิ่งมีตัวเลือกบรรทัดคำสั่งเพื่อควบคุม (-a / - ข้อความ)
Will Sheppard

2

การตอบคำถามจริง ๆ "อะไรทำให้ grep พิจารณาว่าไฟล์เป็นไบนารี?" คุณสามารถใช้iconv:

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

ในกรณีของฉันมีตัวอักษรภาษาสเปนที่ปรากฏขึ้นอย่างถูกต้องในโปรแกรมแก้ไขข้อความ แต่ grep ถือว่าเป็นไบนารี iconvผลลัพธ์ชี้ไปที่หมายเลขบรรทัดและคอลัมน์ของอักขระเหล่านั้น

ในกรณีของNULตัวละครiconvจะพิจารณาตามปกติและจะไม่พิมพ์ผลลัพธ์ประเภทนั้นดังนั้นวิธีนี้จึงไม่เหมาะสม


1

ฉันมีปัญหาเดียวกัน. ฉันเคยvi -b [filename]เห็นตัวละครที่เพิ่มเข้ามา ผมพบว่าตัวละครที่ควบคุมและ^@ ^Mจากนั้นในประเภท vi :1,$s/^@//gเพื่อลบ^@ตัวละคร ^Mทำซ้ำคำสั่งนี้

คำเตือน: การได้รับ "สีฟ้า" การควบคุมตัวละครกดCtrl+ vแล้วCtrl+ Mหรือ+Ctrl @จากนั้นบันทึกและออกจาก vi

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