क्या grep उत्पादन केवल निर्दिष्ट समूहों से मेल खा सकता है?


289

कहो कि मेरे पास एक फाइल है:

# file: 'test.txt'
foobar bash 1
bash
foobar happy
foobar

मैं केवल यह जानना चाहता हूं कि "फोब्बर" के बाद कौन से शब्द दिखाई देते हैं, इसलिए मैं इस रेगेक्स का उपयोग कर सकता हूं:

"foobar \(\w\+\)"

कोष्ठक इंगित करता है कि मुझे फोबार के ठीक बाद के शब्द में विशेष रुचि है। लेकिन जब मैं एक करता grep "foobar \(\w\+\)" test.txtहूं, मुझे पूरी पंक्तियाँ मिलती हैं जो पूरे रेक्स से मेल खाती हैं, न कि केवल "फोब्बर के बाद का शब्द":

foobar bash 1
foobar happy

मैं बहुत पसंद करूँगा कि उस कमांड का आउटपुट इस तरह दिखे:

bash
happy

क्या केवल नियमित अभिव्यक्ति में समूहीकरण (या एक विशिष्ट समूह) से मेल खाने वाली वस्तुओं का उत्पादन करने के लिए grep बताने का कोई तरीका है?


4
उन लोगों के लिए जिन्हें grep की आवश्यकता नहीं है:perl -lne 'print $1 if /foobar (\w+)/' < test.txt
vault

जवाबों:


324

GNU grep में -Pperl-style regexes का -oविकल्प है , और केवल जो पैटर्न से मेल खाता है उसे प्रिंट करने का विकल्प है। इन्हें grep पैटर्न के उस हिस्से को हटाने के लिए लुक-अराउंड असेसरीज ( perlre manpage में विस्तारित पैटर्न के तहत वर्णित) का उपयोग करके जोड़ा जा सकता है , जिसके उद्देश्यों के लिए मिलान किया जाना निर्धारित है -o

$ grep -oP 'foobar \K\w+' test.txt
bash
happy
$

\Kलघु-रूप (और अधिक कुशल फार्म) की है (?<=pattern)जो आप एक शून्य चौड़ाई लुक-पीछे पाठ आप उत्पादन करना चाहते हैं इससे पहले कि दावे के रूप में इस्तेमाल करते हैं। (?=pattern)पाठ को आउटपुट करने के बाद शून्य-चौड़ाई के रूप में उपयोग किए जाने के दावे के रूप में उपयोग किया जा सकता है।

उदाहरण के लिए, यदि आप के बीच शब्द मिलान करना चाहते थे fooऔर bar, आप इस्तेमाल कर सकते हैं:

$ grep -oP 'foo \K\w+(?= bar)' test.txt

या (समरूपता के लिए)

$ grep -oP '(?<=foo )\w+(?= bar)' test.txt

3
यदि आपका regex एक समूहीकरण से अधिक है तो आप इसे कैसे करते हैं? (जैसा कि शीर्षक निहित है?)
बैरासेल

4
@barracel: मुझे विश्वास नहीं होता कि आप कर सकते हैं। समयsed(1)
कैम

1
@camh मैंने अभी परीक्षण किया है कि grep -oP 'foobar \K\w+' test.txtओपी के साथ कुछ भी आउटपुट नहीं है test.txt। Grep संस्करण 2.5.1 है। क्या गलत हो सकता है? O_O
SOUser

@ XichenLi: मैं नहीं कह सकता। मैंने अभी grep का v2.5.1 बनाया है (यह 2006 से बहुत पुराना है) और इसने मेरे लिए काम किया।
कैम

@ उपयोगकर्ता: मैंने वही अनुभव किया - फाइल करने के लिए कुछ भी नहीं। मैंने आउटपुट भेजने के लिए फ़ाइल नाम से पहले '>' को शामिल करने के लिए संपादन अनुरोध प्रस्तुत किया क्योंकि यह मेरे लिए काम करता था।
rjchicago

39

मानक grep ऐसा नहीं कर सकता, लेकिन GNU grep के हाल के संस्करण कर सकते हैं । आप sed, awk या perl की ओर मुड़ सकते हैं। यहां कुछ उदाहरण दिए गए हैं जो आप अपने नमूना इनपुट पर चाहते हैं; वे कोने के मामलों में थोड़ा अलग व्यवहार करते हैं।

foobar word other stuffद्वारा प्रतिस्थापित करें word, केवल तभी प्रिंट करें जब कोई प्रतिस्थापन किया जाता है।

sed -n -e 's/^foobar \([[:alnum:]]\+\).*/\1/p'

यदि पहला शब्द है foobar, तो दूसरा शब्द प्रिंट करें।

awk '$1 == "foobar" {print $2}'

foobarअगर यह पहला शब्द है, तो स्ट्रिप करें और अन्यथा लाइन छोड़ें; फिर पहले व्हाट्सएप और प्रिंट के बाद सब कुछ पट्टी करें।

perl -lne 's/^foobar\s+// or next; s/\s.*//; print'

बहुत बढ़िया! मुझे लगा कि मैं इसे सेड के साथ करने में सक्षम हो सकता हूं, लेकिन मैंने पहले इसका इस्तेमाल नहीं किया था और मुझे उम्मीद थी कि मैं अपने परिचित का उपयोग कर सकता हूं grep। लेकिन इन आदेशों के लिए वाक्यविन्यास वास्तव में अब बहुत परिचित लग रहा है कि मैं विम-स्टाइल खोज और रिप्लेस + रीगेक्स से परिचित हूं। अनेक अनेक धन्यवाद।
कोरी क्लेन

1
सच नहीं, गाइल्स। GNU grep समाधान के लिए मेरा उत्तर देखें।
कैम

1
@camh: आह, मुझे नहीं पता कि GNU grep के पास अब पूरा PCRE सपोर्ट नहीं था। मैंने अपना उत्तर सही कर दिया है, धन्यवाद।
गाइल्स

1
यह जवाब विशेष रूप से एम्बेडेड लिनक्स के लिए उपयोगी है क्योंकि बिजीबॉक्स grepमें पीसीआरई सपोर्ट नहीं है।
क्रेग मैकक्वीन

जाहिर है प्रस्तुत कार्य को पूरा करने के कई तरीके हैं, हालांकि, अगर ओपी जीआरपी उपयोग के लिए कहता है, तो आप कुछ और क्यों जवाब देते हैं? इसके अलावा, आपका पहला पैराग्राफ गलत है: हाँ grep इसे कर सकता है।
fcm

32
    sed -n "s/^.*foobar\s*\(\S*\).*$/\1/p"

-n     suppress printing
s      substitute
^.*    anything before foobar
foobar initial search match
\s*    any white space character (space)
\(     start capture group
\S*    capture any non-white space character (word)
\)     end capture group
.*$    anything after the capture group
\1     substitute everything with the 1st capture group
p      print it

1
Sed उदाहरण के लिए +1, grep की तुलना में नौकरी के लिए एक बेहतर उपकरण लगता है। एक टिप्पणी, ^और लालची मैच के $बाद से बहिर्मुखी हैं .*। हालांकि, उनमें से रेगेक्स के इरादे को स्पष्ट करने में मदद मिल सकती है।
टोनी

18

ठीक है, यदि आप जानते हैं कि फ़ॉबर हमेशा पहला शब्द या लाइन है, तो आप कट का उपयोग कर सकते हैं। इस तरह:

grep "foobar" test.file | cut -d" " -f2

-oग्रेप पर स्विच व्यापक रूप से (तो और अधिक ग्नू ग्रेप एक्सटेंशन से) लागू नहीं कर रहा हो grep -o "foobar" test.file | cut -d" " -f2इस समाधान है, जो lookbehind कथनों का उपयोग कर अधिक से अधिक पोर्टेबल है की प्रभावशीलता में वृद्धि होगी।
डब्यूजिम

मुझे विश्वास है कि आपको grep -o "foobar .*"या की आवश्यकता होगी grep -o "foobar \w+"
जी-मैन

9

यदि PCRE समर्थित नहीं है, तो आप grep के दो इनवोकेशन के साथ एक ही परिणाम प्राप्त कर सकते हैं। फोब्बर के बाद इस शब्द को खींचने के लिए उदाहरण के लिए :

<test.txt grep -o 'foobar  *[^ ]*' | grep -o '[^ ]*$'

इसे इस तरह से फ़ॉबर के बाद एक अनियंत्रित शब्द तक विस्तारित किया जा सकता है (पठनीयता के लिए ईआरई के साथ):

i=1
<test.txt egrep -o 'foobar +([^ ]+ +){'$i'}[^ ]+' | grep -o '[^ ]*$'

आउटपुट:

1

ध्यान दें कि सूचकांक iशून्य-आधारित है।


6

pcregrepएक होशियार -oविकल्प है जो आपको उन समूहों को चुनने देता है जो आप आउटपुट चाहते हैं। इसलिए, अपनी उदाहरण फ़ाइल का उपयोग करते हुए,

$ pcregrep -o1 "foobar (\w+)" test.txt
bash
happy

4

उपयोग करना grepक्रॉस-प्लेटफॉर्म संगत नहीं है, क्योंकि -P/ --perl-regexpकेवल जीएनयूgrep पर उपलब्ध है , बीएसडीgrep नहीं ।

यहाँ समाधान का उपयोग कर रहा है ripgrep:

$ rg -o "foobar (\w+)" -r '$1' <test.txt
bash
happy

प्रति के रूप में man rg:

-r/ --replace REPLACEMENT_TEXTदिए गए पाठ के साथ हर मैच को बदलें।

रिप्लेसमेंट स्ट्रिंग में ग्रुप इंडेक्स (जैसे, $5) और नाम (जैसे) कैप्चर किए $fooजाते हैं।

संबंधित: GH-462


2

मुझे @jgshawkey का जवाब बहुत मददगार लगा। grepइसके लिए इतना अच्छा साधन नहीं है, लेकिन sed है, हालांकि यहां हमारे पास एक उदाहरण है जो एक प्रासंगिक रेखा को हथियाने के लिए grep का उपयोग करता है।

यदि आप इसके लिए अभ्यस्त नहीं हैं, तो रेक्स का रेक्सक्स सिंटैक्स आइडियोसिंक्रेटिक है।

यहां एक और उदाहरण दिया गया है: यह एक आईडी पूर्णांक प्राप्त करने के लिए xinput का आउटपुट देता है

⎜   ↳ SynPS/2 Synaptics TouchPad                id=19   [slave  pointer  (2)]

और मुझे 19 चाहिए

export TouchPadID=$(xinput | grep 'TouchPad' | sed  -n "s/^.*id=\([[:digit:]]\+\).*$/\1/p")

वर्ग सिंटैक्स पर ध्यान दें:

[[:digit:]]

और निम्नलिखित से बचने की जरूरत है +

मैं केवल एक पंक्ति से मेल खाता हूं।


यह वही है जो मैं करने की कोशिश कर रहा था। धन्यवाद!
जेम्स

अतिरिक्त के बिना थोड़ा सरल संस्करण grep, 'आईपैड' के 'आईडी' के बाईं ओर है:echo "SynPS/2 Synaptics TouchPad id=19 [slave pointer (2)]" | sed -nE "s/.*TouchPad.+id=([0-9]+).*/\1/p"
अमित नायडू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.