मैं कई लाइनों में "grep" पैटर्न कैसे कर सकता हूं?


24

ऐसा लगता है कि मैं दुरुपयोग कर रहा हूं grep/ egrep

मैं कई लाइन में तार खोजने की कोशिश कर रहा था और मुझे मैच नहीं मिल रहा था, जबकि मुझे पता है कि मैं जो देख रहा हूं वह मैच होना चाहिए। मूल रूप से मैंने सोचा था कि मेरे रेगेक्स गलत थे, लेकिन मैंने अंततः पढ़ा कि ये उपकरण प्रति लाइन काम करते हैं (यह भी कि मेरे रेग्जेस इतने तुच्छ थे कि यह मुद्दा नहीं हो सकता)।

तो कौन सा उपकरण कई लाइनों में पैटर्न खोजने के लिए उपयोग करेगा?



1
@CiroSantilli - मुझे नहीं लगता कि यह क्यू और आप जिस से जुड़े हुए हैं डुप्लिकेट हैं। अन्य प्रश्न पूछ रहा है कि आप मल्टी-लाइन पैटर्न मैच कैसे करेंगे (यानी मुझे ऐसा करने के लिए कौन सा टूल / उपकरण का उपयोग करना चाहिए) जबकि यह पूछ रहा है कि यह कैसे करना है grep। वे कसकर संबंधित हैं, लेकिन डीओ नहीं हैं, आईएमओ।
SLM

@ उन मामलों को तय करना मुश्किल है: मैं आपकी बात देख सकता हूं। मुझे लगता है कि यह विशेष मामला डुप्लिकेट के रूप में बेहतर है क्योंकि उपयोगकर्ता ने कहा "grep"कि क्रिया को "grep" करने का सुझाव दिया गया है, और स्वीकृत सहित शीर्ष उत्तर, grep का उपयोग नहीं करते हैं।
सिरो संतिली 改造 改造 iro i 事件

जवाबों:


24

यहाँ sedएक है जो आपको grepकई लाइनों में समान व्यवहार देगा :

sed -n '/foo/{:start /bar/!{N;b start};/your_regex/p}' your_file

यह काम किस प्रकार करता है

  • -n हर पंक्ति को छापने के डिफ़ॉल्ट व्यवहार को दबा देता है
  • /foo/{}यह मिलान करने के लिए निर्देश देता है fooऔर वही करता है जो स्क्वैगलीज़ के अंदर मिलान लाइनों तक आता है। fooपैटर्न के शुरुआती हिस्से से बदलें ।
  • :start जब तक हम अपने रेगेक्स का अंत नहीं पाते, तब तक हमें लूपिंग रखने में मदद करने के लिए एक शाखा है।
  • /bar/!{}जो कुछ मेल नहीं खाता है, उसके लिए स्क्विगलीज़ में क्या निष्पादित होगा barbarपैटर्न के अंत भाग के साथ बदलें ।
  • Nसक्रिय बफ़र के लिए अगली पंक्ति जोड़ता है ( sedइसे पैटर्न स्पेस कहता है)
  • b startबिना startलेबल वाली शाखा को हमने पहले बनाया था ताकि अगली पंक्ति को तब तक लागू रखा जा सके जब तक पैटर्न स्पेस शामिल न हो bar
  • /your_regex/pयदि यह मेल खाता है तो पैटर्न स्पेस प्रिंट करता है your_regex। आपको your_regexउस संपूर्ण अभिव्यक्ति से प्रतिस्थापित करना चाहिए जिसे आप कई लाइनों में मेल खाना चाहते हैं।

1
+1 टूलकिट में इसे जोड़ना! धन्यवाद।
wmorrison365

नोट: मैकओएस पर यह देता हैsed: 1: "/foo/{:start /bar/!{N;b ...": unexpected EOF (pending }'s)
स्टेन जेम्स

1
sed: unterminated {त्रुटि प्राप्त करना
Nomaed

@ यहाँ अंधेरे में गोली मार दी, लेकिन अपने regex किसी भी "{" अक्षर होते हैं? यदि ऐसा है, तो आपको उन्हें वापस भागने की आवश्यकता होगी।
जोसफ आर।

1
@ नोमाडेड ऐसा लगता है कि इसे कार्यान्वयन के बीच के अंतर के साथ sedकरना है। मैंने उपरोक्त स्क्रिप्ट को मानक-अनुरूप बनाने के लिए उस उत्तर में सिफारिशों का पालन करने की कोशिश की, लेकिन इसने मुझे बताया कि "स्टार्ट" एक अपरिभाषित लेबल था। इसलिए मुझे यकीन नहीं है कि यह एक मानक-अनुरूप तरीके से किया जा सकता है। यदि आप इसे प्रबंधित करते हैं, तो कृपया मेरे उत्तर को संपादित करने के लिए स्वतंत्र महसूस करें।
जोसेफ आर

19

मैं आम तौर पर एक उपकरण कहा जाता है का उपयोग करें pcregrepजो linux स्वाद के सबसे का उपयोग करने में स्थापित किया जा सकता yumया apt

उदाहरण के लिए।

मान लीजिए यदि आपके पास testfileसामग्री के साथ नाम वाली फ़ाइल है

abc blah
blah blah
def blah
blah blah

आप निम्न आदेश चला सकते हैं:

$ pcregrep -M  'abc.*(\n|.)*def' testfile

कई लाइनों में पैटर्न मिलान करने के लिए।

इसके अलावा, आप के साथ भी ऐसा ही कर सकते हैं sed

$ sed -e '/abc/,/def/!d' testfile

5

यहाँ पर्ल का उपयोग करते हुए एक सरल तरीका है:

perl -e '$f=join("",<>); print $& if $f=~/foo\nbar.*\n/m' file

या (जब से जोसेफ ने sedमार्ग लिया , मैं बेशर्मी से उनका सुझाव चुरा लूंगा )

perl -n000e 'print $& while /^foo.*\nbar.*\n/mg' file

व्याख्या

$f=join("",<>);: यह संपूर्ण फ़ाइल को पढ़ता है और सामग्री (newlines और सभी) को चर में सहेजता है $f। हम तब मिलान करने का प्रयास करते हैं foo\nbar.*\n, और इसे प्रिंट करते हैं यदि यह मेल खाता है (विशेष चर $&अंतिम मैच पाया जाता है)। ///mनई पंक्तियां भर में रेगुलर एक्सप्रेशन मिलान बनाने के लिए की जरूरत है।

-0इनपुट रिकॉर्ड विभाजक सेट। इसे 00'पैराग्राफ मोड' को सक्रिय करने के लिए सेट करना जहां पर्ल \n\nविभाजक के रूप में लगातार नईलाइन ( ) का उपयोग करेगा । ऐसे मामलों में जहां लगातार नई लाइनें नहीं होती हैं, एक ही बार में पूरी फ़ाइल पढ़ी जाती है (झुकी हुई)।

चेतावनी:

है बड़ी फ़ाइलों के लिए ऐसा करते हैं, यह स्मृति में पूरी फ़ाइल लोड होगा और यह एक समस्या हो सकती है।


2

ऐसा करने का एक तरीका पर्ल के साथ है। उदाहरण के लिए यहां एक फ़ाइल नाम की सामग्री है foo:

foo line 1
bar line 2
foo
foo
foo line 5
foo
bar line 6

अब, यहाँ कुछ पर्ल है जो किसी भी लाइन के खिलाफ मेल खाता है जो फू के साथ शुरू होता है और उसके बाद किसी भी लाइन से शुरू होता है:

cat foo | perl -e 'while(<>){$all .= $_}
  while($all =~ /^(foo[^\n]*\nbar[^\n]*\n)/m) {
  print $1; $all =~ s/^(foo[^\n]*\nbar[^\n]*\n)//m;
}'

पर्ल, टूट गया:

  • while(<>){$all .= $_} यह चर में पूरे मानक इनपुट को लोड करता है $all
  • while($all =~जबकि चर allकी नियमित अभिव्यक्ति होती है ...
  • /^(foo[^\n]*\nbar[^\n]*\n)/mरेगेक्स: लाइन की शुरुआत में फू, उसके बाद किसी भी संख्या में गैर-न्यूलाइन वर्ण, उसके बाद एक नई लाइन, उसके तुरंत बाद "बार" और उसके साथ बार के साथ बाकी की लाइन। /mरेगेक्स के अंत में "कई लाइनों के बीच मैच" का अर्थ है
  • print $1 रेगेक्स के उस हिस्से को प्रिंट करें जो कोष्ठक में था (इस मामले में, पूरी नियमित अभिव्यक्ति)
  • s/^(foo[^\n]*\nbar[^\n]*\n)//m रेगेक्स के लिए पहला मैच मिटा दें, इसलिए हम रेगेक्स के कई मामलों को फाइल में विचाराधीन कर सकते हैं

और आउटपुट:

foo line 1
bar line 2
foo
bar line 6

3
बस कहने के लिए अपने पर्ल को और अधिक मुहावरे से छोटा किया जा सकता है:perl -n0777E 'say $& while /^foo.*\nbar.*\n/mg' foo
जोसेफ आर।

2

Grep वैकल्पिक sift मल्टीलाइन मिलान (अस्वीकरण: मैं लेखक हूं) का समर्थन करता है।

मान लें testfile:

<पुस्तक>
  <शीर्षक> लोरम इप्सम </ शीर्षक>
  <विवरण> लोरम इप्सम डोलर अमेट, कंसेटेटुर
  एलीपिसिंग एलीट, सेड इइसमोड अस्थाई एनिडिडंट यूट
  लेबोर एट डोलोर मैग्ना एलिके </ विवरण>
</ पुस्तक>


sift -m '<description>.*?</description>' (विवरण युक्त लाइनें दिखाएं)

परिणाम:

testfile: <विवरण> लोरम इप्सम डोलर अमेट, कंसेटेटुर
टेस्टफाइल: एलीफिसिंग एलीट, सेड इइसमोड अस्थायी इंसीपिडंट यूट
testfile: labore et dolore magna aliqua </ description>


sift -m '<description>(.*?)</description>' --replace 'description="$1"' --no-filename (विवरण निकालने और सुधारना)

परिणाम:

विवरण = "लोरम इप्सम डोलर अमेट, कंसेटेटुर
  एलीपिसिंग एलीट, सेड इइसमोड अस्थाई एनिडिडंट यूट
  लेबोर एट डोलोर मैग्ना एलिके "

1
बहुत अच्छा उपकरण। बधाई हो! इसे Ubuntu जैसे वितरण में शामिल करने का प्रयास करें।
लौरेंको

2

बस एक सामान्य grep जो Perl-regexpपैरामीटर का समर्थन करता Pहै यह काम करेगा।

$ echo 'abc blah
blah blah
def blah
blah blah' | grep -oPz  '(?s)abc.*?def'
abc blah
blah blah
def

(?s) DOTALL संशोधक कहा जाता है जो न केवल वर्णों से मेल खाता है, बल्कि रेखा को तोड़ता है।


जब मैं इस समाधान का प्रयास करता हूं तो आउटपुट 'डिफ' पर समाप्त नहीं होता है, लेकिन फाइल के अंत में जाता है 'ब्लाह'
बकरी

शायद आपका grep -Pविकल्प का समर्थन नहीं करता है
Avinash Raj

1

मैंने grep और -एक अन्य grep विकल्प के साथ मेरे लिए यह एक हल किया।

grep first_line_word -A 1 testfile | grep second_line_word

-एक 1 विकल्प पाया लाइन के बाद 1 लाइन प्रिंट करता है। बेशक यह आपकी फ़ाइल और शब्द संयोजन पर निर्भर करता है। लेकिन मेरे लिए यह सबसे तेज और विश्वसनीय समाधान था।


अलियास ग्रेप = 'grep --color = auto -B10 -A20 -i' तो cat somefile | grepp ब्लाह | grepp फू | grepp बार ... हाँ उन -ए और बी बहुत काम कर रहे हैं ... आपके पास सबसे अच्छा जवाब है
स्कॉट स्टेंसलैंड

1

मान लें कि हमारे पास फ़ाइल test.txt युक्त है:

blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla

निम्नलिखित कोड का उपयोग किया जा सकता है:

sed -n '/foo/,/bar/p' test.txt

निम्नलिखित आउटपुट के लिए:

foo
here
is the
text
to keep between the 2 patterns
bar

1

यदि हम स्वयं को छोड़कर 2 पैटर्न के बीच पाठ प्राप्त करना चाहते हैं।

मान लें कि हमारे पास फ़ाइल test.txt युक्त है:

blabla
blabla
foo
here
is the
text
to keep between the 2 patterns
bar
blabla
blabla

निम्नलिखित कोड का उपयोग किया जा सकता है:

 sed -n '/foo/{
 n
 b gotoloop
 :loop
 N
 :gotoloop
 /bar/!{
 h
 b loop
 }
 /bar/{
 g
 p
 }
 }' test.txt

निम्नलिखित आउटपुट के लिए:

here
is the
text
to keep between the 2 patterns

यह कैसे काम करता है, आइए इसे चरणबद्ध करें

  1. /foo/{ ट्रिगर होता है जब लाइन में "फू" होता है
  2. n पैटर्न लाइन को अगली पंक्ति के साथ बदलें, अर्थात "यहाँ" शब्द
  3. b gotoloop "गेटोलोप" लेबल की शाखा
  4. :gotoloop "गेटोलोप" लेबल को परिभाषित करता है
  5. /bar/!{ यदि पैटर्न में "बार" नहीं है
  6. h होल्ड स्पेस को पैटर्न से बदलें, इसलिए "यहाँ" को होल्ड स्पेस में सहेजा जाता है
  7. b loop "पाश" लेबल की शाखा
  8. :loop "लूप" लेबल को परिभाषित करता है
  9. N पैटर्न को होल्ड स्पेस में जोड़ता है।
    अब होल्ड स्पेस में शामिल है:
    "यहाँ"
    ""
  10. :gotoloop हम अब चरण 4 पर हैं, और एक पंक्ति में "बार" होने तक लूप करते हैं
  11. /bar/ लूप समाप्त हो गया है, "बार" मिल गया है, यह पैटर्न स्पेस है
  12. g पैटर्न स्पेस को होल्ड स्पेस से बदल दिया जाता है जिसमें "फू" और "बार" के बीच की सभी लाइनें होती हैं जो मुख्य लूप के दौरान बच जाती हैं
  13. p मानक आउटपुट के लिए प्रतिमान स्थान की प्रतिलिपि बनाएँ

किया हुआ !


अच्छा किया, +1। मैं आमतौर पर SOH में newlines tr'ing द्वारा इन आदेशों का उपयोग करने से बचता हूं और फिर सामान्य sed कमांड करता है और फिर newlines को प्रतिस्थापित करता हूं।
A.Danischewski
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.