grep: स्मृति समाप्त हो गई


42

मैं एक बहुत ही सरल खोज कर रहा था:

grep -R Milledgeville ~/Documents

और कुछ समय बाद यह त्रुटि दिखाई दी:

grep: memory exhausted

इससे कैसे बचा जा सकता है?

मेरे पास अपने सिस्टम पर 10GB रैम है और कुछ एप्लिकेशन चल रहे हैं, इसलिए मैं वास्तव में एक साधारण grep स्मृति से बाहर चलाता हूं। ~/Documentsलगभग 100GB है और इसमें सभी प्रकार की फाइलें हैं।

grep -RI यह समस्या नहीं हो सकती है, लेकिन मैं बाइनरी फ़ाइलों में भी खोज करना चाहता हूं।

जवाबों:


46

दो संभावित समस्याएं:

  • grep -R( grepओएस / एक्स 10.8 और इसके बाद के संस्करण पर पाए गए संशोधित जीएनयू को छोड़कर , सिम्बलिंक्स का अनुसरण करता है, इसलिए भले ही फाइलों में केवल 100 ~/Documentsजीबी फाइलें हों , /उदाहरण के लिए अभी भी सिमलिंक हो सकता है और आप फाइलों सहित पूरे फाइल सिस्टम को स्कैन करेंगे। पसंद है /dev/zerogrep -rनए GNU के साथ प्रयोग करें grep, या मानक सिंटैक्स का उपयोग करें:

    find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
    

    (हालांकि ध्यान दें कि निकास स्थिति इस तथ्य को प्रतिबिंबित नहीं करेगी कि पैटर्न मिलान किया गया है या नहीं)।

  • grepपैटर्न से मेल खाने वाली रेखाओं को खोजता है। उसके लिए, एक बार में एक लाइन को मेमोरी में लोड करना होगा। grepकई अन्य grepकार्यान्वयनों के विपरीत GNU में बाइनरी फ़ाइलों में खोज को पढ़ने और समर्थन करने वाली लाइनों के आकार की सीमा नहीं होती है। इसलिए, यदि आपको बहुत बड़ी लाइन (यानी दो न्यूलाइन वर्णों के साथ बहुत दूर तक की एक फ़ाइल) मिली है, जो उपलब्ध मेमोरी से बड़ी है, तो यह विफल हो जाएगी।

    यह आमतौर पर एक विरल फ़ाइल के साथ होता है। आप इसे पुन: पेश कर सकते हैं:

    truncate -s200G some-file
    grep foo some-file
    

    कि एक के आसपास काम करना मुश्किल है। आप इसे (अभी भी GNU के साथ grep) कर सकते हैं:

    find ~/Documents -type f -exec sh -c 'for i do
      tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0"
      done' Milledgeville {} +
    

    इनपुट को खिलाने से पहले NUL वर्णों के अनुक्रमों को एक नई रेखा वर्ण में परिवर्तित करता है grep। यह उन मामलों के लिए कवर होता है जहाँ समस्या विरल फाइलों के कारण होती है।

    आप इसे केवल बड़ी फ़ाइलों के लिए अनुकूलित कर सकते हैं:

    find ~/Documents -type f \( -size -100M -exec \
      grep -He Milledgeville {} + -o -exec sh -c 'for i do
      tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0"
      done' Milledgeville {} + \)
    

    यदि फ़ाइलें विरल नहीं हैं और आपके पास GNU का एक संस्करण grepहै 2.6, तो आप --mmapविकल्प का उपयोग कर सकते हैं । लाइनों को कॉपी किए जाने के विपरीत मेमोरी में एमएमपीड किया जाएगा, जिसका अर्थ है कि सिस्टम हमेशा पेज को फ़ाइल में पेजिंग करके मेमोरी को पुनः प्राप्त कर सकता है। वह विकल्प GNU grep2.6 में हटा दिया गया था


असल में, GNU grep 1 लाइन में पढ़ने के बारे में परवाह नहीं करता है, यह फाइल के एक बड़े हिस्से को सिंगल बफर में पढ़ता है। "इसके अलावा, GNU grep AVOIDS INPUT INTO LINES का निर्माण करता है।" स्रोत: lists.freebsd.org/pipermail/freebsd-current/2010-August/…
Godric Seer

4
@GodricSeer, यह अभी भी फ़ाइल के एक बड़े हिस्से को एक एकल बफर में पढ़ सकता है, लेकिन अगर इसमें स्ट्रिंग नहीं मिली है और इसमें कोई नया वर्ण नहीं मिला है, तो मेरी शर्त यह है कि यह उस एकल बफर को स्मृति में रखता है और अगले बफ़र को पढ़ता है, क्योंकि यदि कोई मिलान मिलता है, तो उसे प्रदर्शित करना होगा। तो, समस्या अभी भी वही है। व्यवहार में, 200GB विरल फ़ाइल पर एक grep OOM के साथ विफल होता है।
स्टीफन चेज़लस

1
@GodricSeer, खैर नहीं। यदि लाइनें सभी छोटी हैं, grepतो अब तक संसाधित किए गए बफ़र्स को त्याग सकते हैं। आप कुछ किलोबाइट से अधिक मेमोरी का उपयोग किए बिना अनिश्चित काल तक grepआउटपुट कर सकते हैं yes। समस्या है लाइनों के आकार।
स्टीफन चेज़लस

3
GNU grep --null-dataविकल्प भी यहाँ उपयोगी हो सकता है। यह इनपुट लाइन टर्मिनेटर के रूप में न्यूलाइन के बजाय एनयूएल के उपयोग को बाध्य करता है।
इरुवर

1
@ 1_CR, अच्छा बिंदु, हालांकि यह NUL को आउटपुट लाइन टर्मिनेटर भी सेट करता है।
स्टीफन चेज़लस

5

मैं आमतौर पर करता हूं

find ~/Documents | xargs grep -ne 'expression'

मैंने कई तरीकों की कोशिश की, और यह सबसे तेज़ पाया। ध्यान दें कि यह रिक्त स्थान के साथ फ़ाइलों को फ़ाइल नाम को बहुत अच्छी तरह से संभाल नहीं करता है। यदि आप जानते हैं कि यह मामला है और grep का GNU संस्करण है, तो आप इसका उपयोग कर सकते हैं:

find ~/Documents -print0 | xargs -0 grep -ne 'expression'

यदि आप उपयोग नहीं कर सकते हैं:

 find ~/Documents -exec grep -ne 'expression' "{}" \;

जो execहर फाइल के लिए एक grep होगा ।


यह रिक्त स्थान वाली फाइलों पर टूट जाएगा।
क्रिस डाउन

हम्म, यह सच है।
कोटे

आप उस के साथfind -print0 | xargs -0 grep -ne 'expression'
द्रविड़न

@ क्रिसडाउन टूटे-पोर्टेबल समाधान के बजाय एक गैर-सुरक्षात्मक समाधान।
रीटो

@ क्रिसडाउन सबसे प्रमुख यूनियनों ने अपनाया है find -print0और xargs -0अब तक: सभी तीन बीएसडी, मिंक 3, सोलारिस 11,…
गाइल्स का एसओ- बुराई को रोकना '

4

मैं इसे पाने के लिए कुछ तरीके सोच सकता हूं:

  • एक ही बार में सभी फ़ाइलों को टटोलने के बजाय, एक समय में एक फ़ाइल करें। उदाहरण:

    find /Documents -type f -exec grep -H Milledgeville "{}" \;
    
  • यदि आपको केवल यह जानने की जरूरत है कि किन फाइलों में शब्द हैं, इसके grep -lबजाय। चूँकि grep पहली हिट के बाद खोजना बंद कर देगा, इसलिए उसे किसी भी विशाल फ़ाइल को पढ़ना नहीं पड़ेगा

  • यदि आप वास्तविक पाठ भी चाहते हैं, तो आप दो अलग-अलग greps के साथ स्ट्रिंग कर सकते हैं:

    for file in $( grep -Rl Milledgeville /Documents ); do grep -H Milledgeville "$file"; done
    

अंतिम उदाहरण वैध सिंटैक्स नहीं है - आपको कमांड प्रतिस्थापन करने की आवश्यकता होगी (और आपको ऐसा नहीं करना चाहिए, क्योंकि grepआउटपुट एक सीमांकक का उपयोग करता है जो फ़ाइल नामों में कानूनी है)। आपको भी उद्धृत करने की आवश्यकता है $file
क्रिस डाउन

उत्तरार्द्ध उदाहरण फ़ाइल नामों के मुद्दे के साथ ग्रस्त है, जिसमें न्यूलाइन या व्हाट्सएप है, (यह forदो तर्कों के रूप में फ़ाइल को संसाधित करने का कारण होगा )
द्रविण

@DravSloan आपका सुधार, जबकि एक सुधार, अभी भी कानूनी फ़ाइल नामों पर टूटता है।
क्रिस डाउन

1
हाँ, मैंने इसे छोड़ दिया क्योंकि यह उसके जवाब का हिस्सा था, मैंने बस इसे सुधारने की कोशिश की ताकि यह चल सके (उन मामलों के लिए जहां फाइलों में कोई रिक्त स्थान / नई सूची आदि नहीं है)।
द्रविण स्लोन

उसका सुधार -> उसका, मेरी माफी जेनी: /
द्रविण

1

मैं खोए हुए डेटा की खोज के लिए 6TB डिस्क पकड़ रहा हूं, और मेमोरी समाप्त हो गई है -रोर। यह अन्य फ़ाइलों के लिए भी काम करना चाहिए।

हम जो समाधान लेकर आए थे, उसमें dd का उपयोग करके डिस्क को chunks में पढ़ा था, और chunks को grepping किया था। यह कोड है (big-grep.sh):

#problem: grep gives "memory exhausted" error on 6TB disks
#solution: read it on parts
if [ -z $2 ] || ! [ -e $1 ]; then echo "$0 file string|less -S # greps in chunks"; exit; fi

FILE="$1"
MATCH="$2"

SIZE=`ls -l $1|cut -d\  -f5`
CHUNKSIZE=$(( 1024 * 1024 * 1 )) 
CHUNKS=100 # greps in (100 + 1) x 1MB = 101MB chunks
COUNT=$(( $SIZE / $CHUNKSIZE * CHUNKS ))

for I in `seq 0 $COUNT`; do
  dd bs=$CHUNKSIZE skip=$(($I*$CHUNKS)) count=$(( $CHUNKS+1)) if=$FILE status=none|grep -UF -a --context 6 "$MATCH"
done

1
जब तक आप ओवरलैपिंग विखंडू नहीं पढ़ते हैं , आप संभवतः चंक सीमाओं पर मैचों को याद करेंगे। ओवरलैप कम से कम उतना बड़ा होना चाहिए जितना कि आप जिस तार से मिलान करने की उम्मीद कर रहे हैं।
Kusalananda

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