क्या grep किसी फ़ाइल को द्विआधारी मानता है?


185

मेरे पास मेरे बॉक्स पर एक विंडोज सिस्टम से कुछ डेटाबेस डंप हैं। वे टेक्स्ट फाइलें हैं। मैं उनके माध्यम से grep करने के लिए cygwin का उपयोग कर रहा हूं। ये सादे पाठ फ़ाइलें प्रतीत होती हैं; मैं उन्हें नोटपैड और वर्डपैड जैसे पाठ संपादकों के साथ खोलता हूं और वे सुपाठ्य लगते हैं। हालांकि, जब मैं उन पर grep चलाऊंगा, तो यह कहेगा binary file foo.txt matches

मैंने देखा है कि फ़ाइलों में कुछ एससीआई NULअक्षर होते हैं , जो मुझे लगता है कि डेटाबेस डंप से कलाकृतियां हैं।

तो क्या grep इन फाइलों को द्विआधारी मानता है? NULचरित्र? क्या फाइलसिस्टम पर एक झंडा है? मुझे लाइन मैच दिखाने के लिए grep पाने के लिए मुझे क्या बदलने की आवश्यकता है?


2
--null-dataयदि NULसीमांकक उपयोगी है।
स्टीव-ओ

जवाबों:


125

यदि NULफ़ाइल में कहीं भी कोई वर्ण है, तो grep इसे बाइनरी फ़ाइल के रूप में मानेगा।

इस तरह एक वर्कअराउंड हो सकता है कि cat file | tr -d '\000' | yourgrepसभी अशक्त को समाप्त करने के लिए, और फिर फ़ाइल के माध्यम से खोज की जाए।


149
... या उपयोग -a/ --text, कम से कम GNU grep के साथ।
derobert

1
@derobert: वास्तव में, कुछ (पुराने) सिस्टम पर, grep लाइनों को देखते हैं, लेकिन इसका आउटपुट प्रत्येक मिलान रेखा को पहली बार में काट देगा NUL(संभवत: यह कारण बनता है कि C का प्रिंटफ़ कहता है और इसे मिलान रेखा देता है?)। इस तरह की प्रणाली पर grep cmd .sh_historyकई खाली लाइनें वापस आ जाएंगी क्योंकि 'cmd' से मेल खाने वाली रेखाएं होती हैं, क्योंकि sh_history NULकी प्रत्येक पंक्ति में प्रत्येक लाइन की शुरुआत के साथ एक विशिष्ट प्रारूप होता है । (लेकिन आपकी टिप्पणी "कम से कम जीएनयू grep पर" शायद सच हो। मेरे पास अभी परीक्षण करने के लिए एक हाथ नहीं है, लेकिन मुझे उम्मीद है कि वे इसे अच्छी तरह से संभाल
लेंगे

4
क्या NUL चरित्र की उपस्थिति एकमात्र मापदंड है? मुझे शक है। यह शायद उससे ज्यादा स्मार्ट है। Ascii 32-126 रेंज के बाहर गिरने वाली कोई भी चीज मेरा अनुमान होगी, लेकिन हमें निश्चित होने के लिए स्रोत कोड को देखना होगा।
माइकल मार्टिनेज

2
मेरी जानकारी विशिष्ट grep उदाहरण के मैन पेज से थी। कार्यान्वयन के बारे में आपकी टिप्पणी मान्य है, स्रोत ट्रम्प डॉक्स।
बीबीजा ४२

2
मेरे पास एक फाइल थी जिसे grepसाइबरविन ने बाइनरी माना था क्योंकि इसमें नियमित ASCII हाइफ़न / माइनस (0x2d) के बजाय एक लंबा डैश (0x96) था। मुझे लगता है कि इस जवाब ने ओपी के मुद्दे को हल कर दिया है, लेकिन ऐसा लगता है कि यह अधूरा है।
cp.engr

121

grep -a मेरे लिए काम किया:

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

4
यह सबसे अच्छा, कम से कम महंगा जवाब IMO है।
11

लेकिन POSIX का अनुपालन नहीं
Matteo

21

आप stringsकिसी भी फ़ाइल से पाठ सामग्री को निकालने के लिए उपयोगिता का उपयोग कर सकते हैं और फिर इसे grepइस तरह से पाइप कर सकते हैं strings file | grep pattern:।


2
लॉग फ़ाइलों को अलग करने के लिए आदर्श जो आंशिक रूप से दूषित हो सकता है
हनीस आर।

हां, कभी-कभी बाइनरी मिश्रित लॉगिंग भी होती है। यह अच्छा है।
sdkks 16

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'
    

    क्योंकि \x80UTF-8 यूनिकोड बिंदु का पहला बाइट नहीं हो सकता: UTF-8 - विवरण | en.wikipedia.org

इसके अलावा, जैसा कि स्टीफन चेज़ेलस ने उल्लेख किया है कि क्या grep किसी फ़ाइल को द्विआधारी मानता है? | यूनिक्स और लिनक्स स्टैक एक्सचेंज , वे चेक केवल लंबाई TODO के पहले बफर रीड तक किए जाते हैं।

केवल पहले बफर तक पढ़ा

इसलिए यदि 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पहली पंक्ति के साथ अगर यह केवल 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

त्वरित ग्रेपिंग से 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जाता है तो वह सच हो जाता है।

TODO कब nlines_first_null < nlinesकभी झूठ हो सकता है? मैं आलसी हो गया।

POSIX

द्विआधारी विकल्प को परिभाषित नहीं करता है grep - एक पैटर्न के लिए एक फ़ाइल खोजें pubs.opengroup.org , और GNU grep इसे प्रलेखित नहीं करते हैं, इसलिए RTFS एकमात्र तरीका है।


1
प्रभावशाली खोज!
user394

2
ध्यान दें कि मान्य UTF-8 के लिए चेक केवल UTF-8 स्थानों में होता है। यह भी ध्यान दें कि चेक केवल उस फ़ाइल से पढ़े गए पहले बफ़र पर किया जाता है, जो एक नियमित फ़ाइल के लिए मेरे सिस्टम पर 32768 बाइट्स लगता है, लेकिन एक पाइप या सॉकेट के लिए एक बाइट जितना छोटा हो सकता है। तुलना (printf '\n\0y') | grep yके साथ (printf '\n'; sleep 1; printf '\0y') | grep yउदाहरण के लिए।
स्टीफन चेज़लस

@ StéphaneChazelas "ध्यान दें कि वैध UTF-8 के लिए चेक केवल UTF-8 स्थानों में होता है": क्या आप export LC_CTYPE='en_US.UTF-8'मेरे उदाहरण में, या कुछ और के बारे में हैं? Buf पढ़ा: अद्भुत उदाहरण, जवाब में जोड़ा गया। आपने स्पष्ट रूप से मेरे से अधिक स्रोत को पढ़ा है, मुझे उन हैकर कॉन्स की याद दिलाता है "छात्र प्रबुद्ध था" :-)
सिरो सेंटिल्ली 改造 source source

1
मैं या तो महान विस्तार में नहीं देखा था, लेकिन हाल ही में किया था
स्टीफन चेज़ेलस

1
@CiroSantilli 巴拿馬 ill ill ill U 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
यह मुझे भी हुआ था। विशेष रूप से, कारण एक आईएसओ-8859-1-एन्कोडेड गैर-ब्रेकिंग स्पेस था, जिसे मुझे फ़ाइल में खोज करने के लिए grep प्राप्त करने के लिए एक नियमित स्थान के साथ बदलना पड़ा।
गालैको

4
grep 2.21 ISO-8859 पाठ फ़ाइलों को मानता है जैसे कि वे बाइनरी हैं, grep कमांड से पहले निर्यात LC_ALL = C जोड़ें।
netawater

@netawater धन्यवाद! यह उदाहरण है कि यदि आपके पास टेक्स्ट-फाइल में मुलर जैसा कुछ है। वह 0xFCहेक्साडेसिमल है, इसलिए रेंज के बाहर grep utf8 (अप 0x7F) के लिए उम्मीद करेगा । Printf 'a \ x7F' के साथ जाँच करें Grep 'a' के रूप में Ciro ऊपर वर्णित है।
ऐनी वैन रोसुम

5

फ़ाइल /etc/magicया /usr/share/misc/magicअनुक्रम की एक सूची है जो कमांड fileफ़ाइल प्रकार का निर्धारण करने के लिए उपयोग करती है।

ध्यान दें कि बाइनरी बस एक कमबैक समाधान हो सकता है। कभी-कभी अजीब एन्कोडिंग वाली फ़ाइलों को भी बाइनरी माना जाता है।

grepलिनक्स पर बाइनरी फ़ाइलों को संभालने के लिए कुछ विकल्प हैं जैसे --binary-filesया-U / --binary


अधिक सटीक रूप से, C99 के अनुसार एन्कोडिंग त्रुटि mbrlen()। उदाहरण और स्रोत की व्याख्या: unix.stackexchange.com/a/276028/32558
Ciro Santilli 中心::: ''

2

मेरे छात्रों में से एक को यह समस्या थी। में एक बग grepहै Cygwin। यदि फ़ाइल में गैर- Ascii वर्ण हैं, grepऔर egrepइसे बाइनरी के रूप में देखें।


यह एक फीचर की तरह लगता है, बग नहीं। विशेष रूप से इसे नियंत्रित करने के लिए एक कमांड-लाइन विकल्प है (-a / --text)
विल शेपर्ड

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 से बाहर निकलें।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.