मुझे हर मैच को अलग से प्रदर्शित करने के लिए "grep -zoP" कैसे मिल सकता है?

Nov 23 2020

मेरे पास इस फॉर्म पर एक फाइल है:

X/this is the first match/blabla
X-this is
the second match-

and here we have some fluff.

और मैं "एक्स" के बाद और उसी मार्कर के बीच दिखाई देने वाली सभी चीजों को निकालना चाहता हूं। इसलिए अगर मेरे पास "एक्स + मैच +" है, तो मैं "मैच" प्राप्त करना चाहता हूं, क्योंकि यह "एक्स" और मार्कर + + के बाद दिखाई देता है।

इसलिए दी गई नमूना फ़ाइल के लिए मैं यह आउटपुट लेना चाहूंगा:

this is the first match

और फिर

this is
the second match

मैं X के बीच की सभी सामग्री को एक मार्कर द्वारा उपयोग करके प्राप्त करने में कामयाब रहा:

grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file

अर्थात्:

  • grep -Po '(?<=X(.))(.|\n)+(?=\1)'एक्स से मैच करने के बाद (something)उस पर कब्जा कर लिया जाता है और अंत में मिलान किया जाता है (?=\1)(मैं यहां अपने उत्तर पर कोड आधारित करता हूं )।
  • नोट मैं (.|\n)एक नई लाइन सहित कुछ भी मैच करने के लिए उपयोग करता हूं, और यह कि मैं -zgrep में भी नई लाइनों के साथ मेल खाता हूं ।

तो यह अच्छी तरह से काम करता है, एकमात्र समस्या आउटपुट के प्रदर्शन से आती है:

$ grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file
this is the first matchthis is
the second match

जैसा कि आप देख सकते हैं, सभी मैच एक साथ दिखाई देते हैं, "यह पहला मैच है" जिसके बाद "यह दूसरा मैच है" जिसमें कोई भी विभाजक नहीं है। मुझे पता है कि यह "-z" के उपयोग से आता है, जो सभी फाइल को लाइनों के एक सेट के रूप में मानता है , प्रत्येक को एक शून्य बाइट (एएससीआईआई एनयूएल चरित्र) द्वारा एक नई पंक्ति ("मैन जीआरपीपी" उद्धृत करते हुए ) के बजाय समाप्त किया जाता है

तो: क्या इन सभी परिणामों को अलग-अलग प्राप्त करने का एक तरीका है?

मैंने GNU Awk में भी कोशिश की:

awk 'match($0, /X(.)(\n|.*)\1/, a) {print a[1]}' file

लेकिन (\n|.*)काम भी नहीं किया।

जवाब

2 tripleee Nov 23 2020 at 20:23

उपयोग का मामला एक तरह से समस्याग्रस्त है, क्योंकि जैसे ही आप मैचों को प्रिंट करते हैं, आप जानकारी खो देते हैं कि वास्तव में विभाजक कहाँ था। लेकिन अगर यह स्वीकार्य है, तो पाइप करने का प्रयास करें xargs -r0

grep -zPo '(?<=X(.))(.|\n)+(?=\1)' file | xargs -r0

ये विकल्प GNU एक्सटेंशन हैं, लेकिन फिर ऐसा है grep -zऔर (ज्यादातर) grep -P, इसलिए शायद यह स्वीकार्य है।

5 Sundeep Nov 23 2020 at 20:16

awk रेगेक्सपी परिभाषा के भीतर बैकरेफर का समर्थन नहीं करता है।

समाधान:

$ grep -zPo '(?s)(?<=X(.)).+(?=\1)' ip.txt | tr '\0' '\n' this is the first match this is the second match # with ripgrep, which supports multiline matching $ rg -NoUP '(?s)(?<=X(.)).+(?=\1)' ip.txt
this is the first match
this is
the second match

के (?s)X(.)\K.+(?=\1)स्थान पर भी उपयोग कर सकते हैं (?s)(?<=X(.)).+(?=\1)। इसके अलावा, आप match+xyz+foobazइनपुट के लिए मिलान से बचने के लिए यहां गैर-लालची मात्रा का उपयोग करना चाह सकते हैंX+match+xyz+foobaz+


साथ में perl

$ perl -0777 -nE 'say $& while(/X(.)\K.+(?=\1)/sg)' ip.txt
this is the first match
this is
the second match
4 anubhava Nov 23 2020 at 22:21

यहाँ एक और ग्नू-ऑक सॉल्यूशन का उपयोग किया जा रहा है RSऔर RT:

awk -v RS='X.' 'ch != "" && n=index($0, ch) { print substr($0, 1, n-1)
}
RT {
   ch = substr(RT, 2, 1)
}' file
this is the first match
this is
the second match
3 EdMorton Nov 23 2020 at 21:51

जीएनयू मल्टी-आरएस, आरटी, और जेनबस () के लिए जाग के साथ और पूरी फ़ाइल को मेमोरी में पढ़ने के लिए बिना:

$ awk -v RS='X.' 'NR>1{print "<" gensub(end".*","",1) ">"} {end=substr(RT,2,1)}' file
<this is the first match>
<this is
the second match>

जाहिर है मैंने "<" और ">" को जोड़ा है ताकि आप देख सकें कि प्रत्येक आउटपुट रिकॉर्ड कहां से शुरू होता है / समाप्त होता है।

ऊपर मानता है कि चरित्र के बाद Xएक गैर पुनरावृत्ति regexp metachar (जैसे नहीं है ., ^, [, आदि) तो YMMV

1 rowboat Nov 24 2020 at 10:09

GNU grep -zशून्य वर्णों के साथ इनपुट / आउटपुट रिकॉर्ड को समाप्त करता है (जैसे अन्य उपकरणों के साथ संयोजन में उपयोगी sort -z)। pcregrep ऐसा नहीं करेगा:

pcregrep -Mo2 '(?s)X(.)(.+?)\1' file

-onumberलुकऑर्ड्स के बजाय उपयोग किया जाता है। ?आलसी क्वांटिफायर जोड़ा (मामले में \1बाद में होता है)।