shell - दो पैटर्न, समावेशी या अनन्य(sed, AWK या पर्ल में) के बीच की पंक्तियों को कैसे प्रिंट करें?




unix perl (5)

मेरे पास निम्नलिखित की तरह एक फ़ाइल है और मैं दो दिए गए पैटर्न PAT1 और PAT2 बीच की पंक्तियों को प्रिंट करना PAT2

1
2
PAT1
3    - first block
4
PAT2
5
6
PAT1
7    - second block
PAT2
8
9
PAT1
10    - third block

मैंने पढ़ा है कि दो मार्कर पैटर्न के बीच की रेखाओं का चयन कैसे करें जो कि awk / sed के साथ कई बार हो सकते हैं लेकिन मैं इस के सभी संभावित संयोजनों को देखने के लिए उत्सुक हूं, या तो पैटर्न को शामिल या बाहर करना।

मैं दो पैटर्न के बीच सभी लाइनों को कैसे प्रिंट कर सकता हूं?


PAT1 और PAT2 के बीच प्रिंट लाइनें

$ awk '/PAT1/,/PAT2/' file
PAT1
3    - first block
4
PAT2
PAT1
7    - second block
PAT2
PAT1
10    - third block

या, चर का उपयोग कर:

awk '/PAT1/{flag=1} flag; /PAT2/{flag=0}' file

यह कैसे काम करता है?

  • /PAT1/ इस पाठ से मेल खाती रेखाएं, साथ ही साथ /PAT2/ करता है।
  • /PAT1/{flag=1} flag उस समय सेट करता है जब PAT1 एक पंक्ति में मिलता है।
  • /PAT2/{flag=0} उस flag अनसेट करता है जब पाठ PAT2 एक पंक्ति में मिलता है।
  • flag डिफ़ॉल्ट क्रिया के साथ एक पैटर्न है, जिसे print $0 : यदि flag 1 के बराबर है तो रेखा मुद्रित होती है। इस तरह, यह उन सभी लाइनों को प्रिंट करेगा जो PAT1 के समय से होती हैं और अगले PAT2 देखी जाती हैं। यह PAT1 के अंतिम मैच से फ़ाइल के अंत तक की पंक्तियों को भी प्रिंट करेगा।

PAT1 और PAT2 के बीच प्रिंट लाइनें - PAT1 और PAT2 शामिल नहीं हैं

$ awk '/PAT1/{flag=1; next} /PAT2/{flag=0} flag' file
3    - first block
4
7    - second block
10    - third block

यह उस रेखा को छोड़ने के लिए next का उपयोग करता है जिसमें PAT1 होता है ताकि यह मुद्रित न हो सके।

next कॉल को ब्लॉक में फेरबदल करके गिराया जा सकता है: awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file awk '/PAT2/{flag=0} flag; /PAT1/{flag=1}' file

PAT1 और PAT2 के बीच प्रिंट लाइनें - PAT1 सहित

$ awk '/PAT1/{flag=1} /PAT2/{flag=0} flag' file
PAT1
3    - first block
4
PAT1
7    - second block
PAT1
10    - third block

बहुत अंत में flag लगाकर, यह PAT1 या PAT2 पर सेट की गई क्रिया को ट्रिगर करता है: PAT1 पर प्रिंट करने के लिए, PAT2 पर प्रिंट करने के लिए नहीं।

PAT1 और PAT2 के बीच प्रिंट लाइनें - PAT2 सहित

$ awk 'flag; /PAT1/{flag=1} /PAT2/{flag=0}' file
3    - first block
4
PAT2
7    - second block
PAT2
10    - third block

बहुत शुरुआत में flag लगाकर, यह उस क्रिया को ट्रिगर करता है जो पहले सेट की गई थी और इसलिए समापन पैटर्न को प्रिंट करता है, लेकिन शुरुआत नहीं।

PAT1 और PAT2 के बीच प्रिंट लाइनें - यदि कोई अन्य PAT2 नहीं होता है, तो अंतिम PAT1 से फ़ाइल के अंत तक लाइनों को छोड़कर

यह एड मॉर्टन द्वारा एक समाधान पर आधारित है।

awk 'flag{
        if (/PAT2/)
           {printf "%s", buf; flag=0; buf=""}
        else
            buf = buf $0 ORS
     }
     /PAT1/ {flag=1}' file

एक-लाइनर के रूप में:

$ awk 'flag{ if (/PAT2/){printf "%s", buf; flag=0; buf=""} else buf = buf $0 ORS}; /PAT1/{flag=1}' file
3    - first block
4
7    - second block

# note the lack of third block, since no other PAT2 happens after it

यह सभी चयनित लाइनों को एक बफर में रखता है जो PAT1 मिलने के क्षण से आबाद हो जाता है। फिर, यह PAT2 मिलने तक निम्न पंक्तियों से भरा रहता है। उस बिंदु में, यह संग्रहीत सामग्री को प्रिंट करता है और बफर को खाली करता है।


क्लासिक sed सॉल्यूशन के बारे में क्या?

PAT1 और PAT2 के बीच प्रिंट लाइनें - PAT1 और PAT2 शामिल करें

sed -n '/PAT1/,/PAT2/p' FILE

PAT1 और PAT2 के बीच प्रिंट लाइनें - PAT1 और PAT2 को बाहर करें

GNU sed
sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' FILE
कोई भी sed
sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p;};}' FILE

या भी (धन्यवाद Sundeep ):

GNU sed
sed -n '/PAT1/,/PAT2/{//!p}' FILE
कोई भी सेड
sed -n '/PAT1/,/PAT2/{//!p;}' FILE

PAT1 और PAT2 के बीच प्रिंट लाइनें - PAT1 शामिल हैं, लेकिन PAT2 नहीं

निम्नलिखित में सिर्फ रेंज स्टार्ट शामिल है:

GNU sed
sed -n '/PAT1/,/PAT2/{/PAT2/!p}' FILE
कोई भी सेड
sed -n '/PAT1/,/PAT2/{/PAT2/!p;}' FILE

PAT1 और PAT2 के बीच प्रिंट लाइनें - PAT2 शामिल करें लेकिन PAT1 नहीं

निम्नलिखित में केवल सीमा अंत शामिल है:

GNU sed
sed -n '/PAT1/,/PAT2/{/PAT1/!p}' FILE
कोई भी सेड
sed -n '/PAT1/,/PAT2/{/PAT1/!p;}' FILE

1 बीएसडी / मैक ओएस एक्स सेड के बारे में ध्यान दें

इस तरह एक आदेश यहाँ:

sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' FILE

एक त्रुटि का उत्सर्जन करेगा:

▶ sed -n '/PAT1/,/PAT2/{/PAT1/!{/PAT2/!p}}' FILE
sed: 1: "/PAT1/,/PAT2/{/PAT1/!{/ ...": extra characters at the end of p command

इस कारण इस उत्तर को एक-लाइनरों के बीएसडी और जीएनयू संस्करणों को शामिल करने के लिए संपादित किया गया है।


यहाँ एक और दृष्टिकोण है

दोनों पैटर्न शामिल करें (डिफ़ॉल्ट)

$ awk '/PAT1/,/PAT2/' file
PAT1
3    - first block
4
PAT2
PAT1
7    - second block
PAT2
PAT1
10    - third block

दोनों पैटर्न को मास्क करें

$ awk '/PAT1/,/PAT2/{if(/PAT2|PAT1/) next; print}' file
3    - first block
4
7    - second block
10    - third block

मुखौटा शुरू पैटर्न

$ awk '/PAT1/,/PAT2/{if(/PAT1/) next; print}' file
3    - first block
4
PAT2
7    - second block
PAT2
10    - third block

मुखौटा अंत पैटर्न

$ awk '/PAT1/,/PAT2/{if(/PAT2/) next; print}' file
PAT1
3    - first block
4
PAT1
7    - second block
PAT1
10    - third block

वैकल्पिक रूप से:

sed '/START/,/END/!d;//d'

यह START और END के बीच के लोगों को छोड़कर सभी लाइनों को हटा देता है, फिर //d पिछले पैटर्न का उपयोग करने के लिए sed के कारण START और END लाइनों को हटा देता है।


मार्कर के बीच मार्कर और लाइनों को प्रिंट करने के लिए पीसीआरई (जहाँ उपलब्ध हो) के साथ grep का उपयोग करना:

$ grep -Pzo "(?s)(PAT1(.*?)(PAT2|\Z))" file
PAT1
3    - first block
4
PAT2
PAT1
7    - second block
PAT2
PAT1
10    - third block
  • -P -रेगेक्सप, पीसीआरई। सभी grep वेरिएंट में नहीं
  • -z इनपुट को लाइनों के एक सेट के रूप में मानते हैं, प्रत्येक को एक नई बाइट के बजाय एक शून्य बाइट द्वारा समाप्त किया जाता है
  • -o ही मिलान करें
  • (?s) DotAll, यानी। डॉट के रूप में अच्छी तरह से newlines पाता है
  • (.*?) घिनौनी खोज
  • \Z केवल स्ट्रिंग के अंत में, या अंत में newline से पहले मैच

मार्कर को छोड़कर मार्करों के बीच प्रिंट लाइनें :

$ grep -Pzo "(?s)(PAT1(.*?)(?=(\nPAT2|\Z)))" file
PAT1
3    - first block
4
PAT1
7    - second block
PAT1
10    - third block
  • (.*?)(?=(\nPAT2|\Z)) nongreedy \nPAT2 और \Z लिए lookahead के साथ खोजें

मार्करों को छोड़कर मार्करों के बीच प्रिंट लाइनें :

$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(?=(\nPAT2|\Z)))" file
3    - first block
4
7    - second block
10    - third block
  • (?<=PAT1\n) PAT1\n लिए सकारात्मक खोज

मार्कर को छोड़कर मार्करों के बीच प्रिंट लाइनें :

$ grep -Pzo "(?s)((?<=PAT1\n)(.*?)(PAT2|\Z))" file
3    - first block
4
PAT2
7    - second block
PAT2
10    - third block





pattern-matching