2014-05-09 7 views
7

मैं वर्तमान निर्देशिका में grep 40k फ़ाइलों की कोशिश कर रहा हूं और मुझे यह त्रुटि मिल रही है।बड़ी संख्या में फाइलों को कैसे grep करने के लिए?

for i in $(cat A01/genes.txt); do grep $i *.kaks; done > A01/A01.result.txt 
-bash: /usr/bin/grep: Argument list too long 

एक सामान्य रूप से grep हजारों फाइलें कैसे करें?

धन्यवाद उपेंद्र

+1

मुझे लगता है कि आप – keyser

उत्तर

23

यह डेविड उदास कर देता है ...

हर कोई अब तक (anubhava को छोड़ कर) गलत है।

शैल स्क्रिप्टिंग किसी भी अन्य प्रोग्रामिंग भाषा की तरह नहीं है क्योंकि कमांड वास्तव में निष्पादित होने से पहले लाइनों की व्याख्या में से अधिकतर शैल की शक्ति से आती है।

के कुछ सरल लेते हैं:

$ set -x 
$ ls 
+ ls 
bar.txt foo.txt fubar.log 
$ echo The text files are *.txt 
echo The text files are *.txt 
> echo The text files are bar.txt foo.txt 
The text files are bar.txt foo.txt 
$ set +x 
$ 

set -x आप देखना चाहते हैं कि खोल वास्तव में ग्लोब interpolates की अनुमति देता है और फिर से गुजरता है कि वापस इनपुट के रूप में आदेश करने के लिए। > उस रेखा को इंगित करता है जिसे वास्तव में आदेश द्वारा निष्पादित किया जा रहा है।

आप देख सकते हैं कि echo कमांड * की व्याख्या नहीं कर रहा है। इसके बजाय, खोल * पकड़ता है और इसे मिलान करने वाली फ़ाइलों के नाम से बदल देता है। फिर और केवल तभी echo कमांड कमांड को निष्पादित करता है।

आप 40K प्लस फ़ाइलें है, और आप grep * करते हैं, आप विस्तार कर रहे हैं कि उन 40,000 से अधिक फ़ाइलों के नाम के *grep से पहले भी निष्पादित करने के लिए एक मौका है, और कहा कि जहां त्रुटि संदेश /usr/bin है/grep: तर्क सूची बहुत लंबी से आ रही है।

$ find . -name "*.kaks" -type f -maxdepth 1 | xargs grep -f A01/genes.txt 

find . -name "*.kaks" -type f -maxdepth 1 अपने *.kaks फ़ाइलों के सभी मिल जाएगा, और -depth 1 केवल मौजूदा निर्देशिका में फ़ाइलों को शामिल करेगा:

सौभाग्य से, यूनिक्स इस दुविधा के चारों ओर एक रास्ता है। -type f सुनिश्चित करता है कि आप केवल फाइलें चुनें, न कि निर्देशिका।

find आदेश पाइप फ़ाइलों के नाम xargs और xargs में grep -f A01/genes.txt आदेश करने के लिए फ़ाइल के नाम में संलग्न कर देगा। हालांकि, xargs में आस्तीन है। यह जानता है कि कमांड लाइन बफर कितना समय है, और कमांड लाइन बफर भरने पर grep निष्पादित करेगा, फिर फ़ाइल की दूसरी श्रृंखला grep पर पास करें। इस तरह, grep को तीन या दस बार निष्पादित किया जाता है (कमांड लाइन बफर के आकार के आधार पर), और हमारी सभी फाइलों का उपयोग किया जाता है।

दुर्भाग्यवश, xargs फ़ाइल नामों के लिए एक विभाजक के रूप में व्हाइटस्पेस का उपयोग करता है। यदि आपकी फ़ाइलों में रिक्त स्थान या टैब हैं, तो आपको xargs के साथ परेशानी होगी। सौभाग्य से, वहाँ एक और ठीक है:

$ find . -name "*.kaks" -type f -maxdepth 1 -print0 | xargs -0 grep -f A01/genes.txt 

-print0find कारण नई पंक्तियों द्वारा अलग नहीं फ़ाइलों के नाम मुद्रित करने के लिए होगा, लेकिन NUL चरित्र द्वारा। -0 पैरामीटर xargsxargs बताता है कि फ़ाइल विभाजक सफेद जगह नहीं है, लेकिन एनयूएल चरित्र है। इस प्रकार, इस मुद्दे को हल करता है।

तुम भी ऐसा भी कर सकता है:

$ find . -name "*.kaks" -type f -maxdepth 1 -exec grep -f A01/genes.txt {} \; 

यह प्रत्येक के लिए grep और बदले xargs क्या करता है की पाया हर फ़ाइल को निष्पादित करेंगे और केवल सभी फाइलों को यह कमांड लाइन पर सामान कर सकते हैं के लिए grep चलाता है। इसका लाभ यह है कि यह पूरी तरह से खोल हस्तक्षेप से बचाता है। हालांकि, यह कम कुशल नहीं हो सकता है या नहीं भी हो सकता है।

प्रयोग करना दिलचस्प होगा और देखें कि कौन सा अधिक कुशल है। आप को देखने के लिए time उपयोग कर सकते हैं:

$ time find . -name "*.kaks" -type f -maxdepth 1 -exec grep -f A01/genes.txt {} \; 

यह आदेश पर अमल और उसके बाद आपको बता कितना समय ले लिया जाएगा। इसे -exec और xargs के साथ आज़माएं और देखें कि तेज़ क्या है। अपनी खोज के बारे में हमें जरूर बताएं।

+1

+1 इस बात को समझाने में आपके द्वारा निवेश किए गए विशाल समय तक है। – anubhava

+1

हाँ, और आप इसके बीच में थे, जबकि आप एक सही जवाब के साथ आया था। पोस्ट करने से ठीक पहले, मुझे अपने _everybody में एक त्वरित संपादन करना गलत_ कथन है। –

+0

@ डेविड .... विस्तृत स्पष्टीकरण के लिए बहुत बहुत धन्यवाद। मैं दो तरीकों से चल रहा हूं और उन्हें समय-समय पर अपडेट करूँगा और जैसे ही मैं समाप्त करूंगा ... – upendra

0

आप grep की पुनरावर्ती सुविधा का उपयोग कर सकते हैं:

for i in $(cat A01/genes.txt); do 
    grep -r $i . 
done > A01/A01.result.txt 

हालांकि अगर आप केवल kaks फ़ाइलों का चयन करना चाहते हैं:

for i in $(cat A01/genes.txt); do 
    find . -iregex '.*\.kaks$' -exec grep $i \; 
done > A01/A01.result.txt 
0

अपने बाहरी अंदर पाश के लिए एक और रखो एक:

for f in *.kaks; do 
    grep -H $i "$f" 
done 

वैसे, क्या आप प्रत्येक फ़ाइल में हर घटना को खोजने में रुचि रखते हैं, या केवल तभी खोज स्ट्रिंग एक या अधिक बार मौजूद है? यदि स्ट्रिंग को जानने के लिए यह "पर्याप्त" है, तो एक या अधिक बार आप grep को "-n 1" निर्दिष्ट कर सकते हैं और यह पहले मैच को खोजने के बाद बाकी फ़ाइल को पढ़ने/खोजना परेशान नहीं करेगा, जो संभावित रूप से संभवतः हो सकता है बहुत समय बचाओ।

+0

के बजाय 'ढूंढ' का उपयोग करते हैं, मैंने अपने @ डेविड को सुझाए गए तरीके का उपयोग किया और यह ठीक काम किया। विकल्प के लिए धन्यवाद ... – upendra

7

आप grep इस तरह के साथ find गठजोड़ कर सकते हैं:

find . -maxdepth 1 -name '*.kaks' -exec grep -H -f A01/genes.txt '{}' \; > A01/A01.result.txt 
+1

यह उत्तर अच्छी तरह से दोनों संभावित समस्याओं को हल करता है: यह कमांड लाइन पर 'बिल्ली ए 01/genes.txt' का विस्तार (ए) से बचाता है और यह (बी) कमांड लाइन पर '* .kaks' का विस्तार करता है । – John1024

+0

धन्यवाद आपकी मदद के लिए @anubhava .... – upendra

संबंधित मुद्दे