मैं दो शब्दों में से किसी एक के लिए लाइनों को कैसे पकड़ सकता हूं, लेकिन दोनों को नहीं?


25

मैं grepकेवल दो शब्दों में से किसी एक को दर्शाने के लिए उपयोग करने की कोशिश कर रहा हूं , यदि उनमें से केवल एक ही पंक्ति में दिखाई देता है, लेकिन यदि वे एक ही पंक्ति में नहीं हैं।

अब तक मैंने कोशिश की है grep pattern1 | grep pattern2 | ...लेकिन मुझे अपेक्षित परिणाम नहीं मिला।


(1) आप "शब्द" और "पैटर्न" के बारे में बात करते हैं। यह किसका है? साधारण शब्द जैसे "क्विक", "ब्राउन" और "फॉक्स", या रेगुलर एक्सप्रेशन जैसे [a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+? (२) क्या होगा यदि एक शब्द / पैटर्न एक पंक्ति में एक से अधिक बार दिखाई देता है (और दूसरा नहीं दिखाई देता है)? क्या यह शब्द एक बार आने वाले शब्द के बराबर है, या क्या यह कई घटनाओं के रूप में गिना जाता है?
जी-मैन का कहना है कि 'मोनिका की बहाली

जवाबों:


59

के अलावा एक उपकरण जाने grepका रास्ता है।

उदाहरण के लिए, पर्ल का उपयोग करना, यह होगा:

perl -ne 'print if /pattern1/ xor /pattern2/'

perl -neस्टड की प्रत्येक पंक्ति पर दी गई कमांड को चलाता है, जो इस मामले में लाइन को प्रिंट करता है यदि यह मेल खाता है /pattern1/ xor /pattern2/, या दूसरे शब्दों में एक पैटर्न से मेल खाता है , लेकिन अन्य (अनन्य या) नहीं।

यह या तो क्रम में पैटर्न के लिए काम करता है, और इसके कई आवृत्तियों की तुलना में बेहतर प्रदर्शन होना चाहिए grep, और साथ ही कम टाइपिंग है।

या, इससे भी कम, awk के साथ:

awk 'xor(/pattern1/,/pattern2/)'

या जाग के संस्करणों के लिए जो नहीं है xor:

awk '/pattern1/+/pattern2/==1`

4
अच्छा - क्या xorकेवल GNU Awk में उपलब्ध है?
स्टीलड्राइवर

9
@steeldriver मुझे लगता है कि यह केवल GNU है, हाँ। या कम से कम यह पुराने संस्करणों पर गायब है। आप के साथ यह जगह ले सकता है /pattern1/+/pattern2/==1ir xorयाद आ रही है।
क्रिस

4
@JimL। आप शब्द सीमाओं ( \b) को स्वयं पैटर्न में रख सकते हैं, अर्थात \bword\b
वंदारेड

4
@vikingsteve यदि आप विशेष रूप से grep का उपयोग करना चाहते हैं, तो यहां बहुत सारे उत्तर हैं। लेकिन उन लोगों के लिए जो सिर्फ काम करवाना चाहते हैं, यह जानना अच्छा है कि ऐसे अन्य उपकरण हैं जो सब कुछ कर सकते हैं grep, लेकिन अधिक से अधिक आसानी से।
क्रिस

3
@vikingsteve मैं दृढ़ता से मानूंगा कि grep समाधान की मांग एक तरह की XY समस्या है
Hagen von Eitzen

30

जीएनयू के साथ grep, आप दोनों शब्दों को पास कर सकते हैं grepऔर फिर दोनों पैटर्न वाली लाइनों को हटा सकते हैं।

$ cat testfile.txt
abc
def
abc def
abc 123 def
1234
5678
1234 def abc
def abc

$ grep -w -e 'abc' -e 'def' testfile.txt | grep -v -e 'abc.*def' -e 'def.*abc'
abc
def

16

के साथ प्रयास करें egrep

egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

3
के रूप में भी लिखा जा सकता हैgrep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
ग्लेन जैकमैन

8
इसके अलावा, grep मैन पेज से नोट करें: Direct invocation as either egrep or fgrep is deprecated- पसंद करेंgrep -E
ग्लेन जैकमैन

यह मेरा ओएस @glennjackman में नहीं है
Grump

1
@ सच में? ओएस क्या है? यहां तक ​​कि POSIX में उल्लेख किया गया है कि grep में विकल्प -fऔर -eविकल्प होने चाहिए, हालांकि पुराने egrepऔर fgrepथोड़ी देर के लिए समर्थित रहेंगे।
terdon

1
@terdon, POSIX POSIX उपयोगिताओं का पथ निर्दिष्ट नहीं करता है। फिर,, मानक grep(है कि समर्थन करता है -F, -E, -e, -fPOSIX की आवश्यकता के रूप में) में है /usr/xpg4/bin। उपयोगिताओं में विशिष्ट /binहैं।
स्टीफन चेज़लस

12

उन grepकार्यान्वयनों के साथ, जो प्रति-सदृश नियमित अभिव्यक्तियों (जैसे pcregrepया GNU या अस्सिटेंट-ओपन grep -P) का समर्थन करते हैं, आप इसे एक grepइनवोकेशन में कर सकते हैं :

grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'

यह उन रेखाओं को खोजता है जो मेल खाती हैं pat1लेकिन नहीं pat2, या pat2नहीं pat1

(?=...)और (?!...)क्रमशः आगे देखो और नकारात्मक देखो आगे ऑपरेटरों। इसलिए तकनीकी रूप से, उपरोक्त विषय की शुरुआत के लिए दिखता है ( ^) बशर्ते इसके बाद .*pat1और इसके बाद .*pat2या इसके साथ pat1और pat2उलट न हो।

यह लाइनों के लिए उप-रूपी है जिसमें दोनों पैटर्न होते हैं क्योंकि उन्हें फिर दो बार देखा जाएगा। आप इसके बजाय अधिक उन्नत पर्ल ऑपरेटरों का उपयोग कर सकते हैं:

grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'

(?(1)yespattern|nopattern)मैच के खिलाफ yespatternअगर 1सेंट पर कब्जा समूह ( ()ऊपर खाली ) मिलान किया, और nopatternअन्यथा। यदि वह ()मेल खाता है, तो इसका मतलब है कि मिलान pat1नहीं हुआ है, इसलिए हम खोज करते हैं pat2(सकारात्मक रूप से आगे देखें), और हम अन्यथा नहीं pat2 खोजते हैं (नकारात्मक रूप से आगे देखें)।

इसके साथ sed, आप इसे लिख सकते हैं:

sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'

आपका पहला समाधान grep: the -P option only supports a single patternकम से कम हर उस प्रणाली पर विफल हो जाता है जिसकी मुझे पहुँच है। आपके दूसरे समाधान के लिए +1, हालाँकि।
क्रिस

1
@ क्रिस, तुम सही हो। यह GNU के लिए एक सीमा है greppcregrepऔर ast- खुले grep में वह समस्या नहीं है। मैंने -eवैकल्पिक आरई ऑपरेटर के साथ कई को बदल दिया है , इसलिए इसे जीएनयू के grepसाथ भी काम करना चाहिए ।
स्टीफन चेज़लस

हाँ, यह अब ठीक काम करता है।
क्रिस

3

बूलियन शब्दों में, आप A xor B की तलाश में हैं, जिसे लिखा जा सकता है

(ए और बी नहीं)

या

(बी और ए नहीं)

यह देखते हुए कि आपके प्रश्न का उल्लेख नहीं है कि आप आउटपुट के क्रम से संबंधित हैं, जब तक कि मिलान लाइनें दिखाई जाती हैं, A xor B का बूलियन विस्तार grep में बहुत सरल है:

$ cat << EOF > foo
> a b
> a
> b
> c a
> c b
> b a
> b c
> EOF
$ grep -w 'a' foo | grep -vw 'b'; grep -w 'b' foo | grep -vw 'a';
a
c a
b
c b
b c

1
यह काम करता है, लेकिन यह फ़ाइल के क्रम को खराब कर देगा।
स्परहॉक

@ श्रावक सच है, हालांकि "हाथापाई" एक कठोर शब्द है। ;) यह क्रम में पहले 'ए' मैचों को सूचीबद्ध करता है, फिर सभी 'बी' मैचों को क्रम में रखता है। ओपी ने आदेश को बनाए रखने में कोई दिलचस्पी नहीं जताई, बस लाइनें दिखाएं। FAWK, अगला कदम हो सकता है sort | uniq
जिम एल।

उचित कॉल; मैं मानता हूं कि मेरी भाषा गलत थी। मेरा मतलब था कि मूल आदेश को बदल दिया जाएगा।
स्पर्हाक

1
@ श्रावक ... और मैंने आपके अवलोकन में पूर्ण प्रकटीकरण के लिए संपादन किया।
जिम एल।

-2

निम्नलिखित उदाहरण के लिए:

# Patterns:
#    apple
#    pear

# Example line
line="a_apple_apple_pear_a"

यह विशुद्ध रूप से साथ किया जा सकता grep -E, uniqऔर wc

# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

यदि grepपर्ल नियमित अभिव्यक्तियों के साथ संकलित किया गया है, तो आप पाइप की आवश्यकता के बजाय अंतिम घटना पर मेल कर सकते हैं uniq:

# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)

परिणाम का परिणाम:

# Only one of the words exists if the result is < 2
((result > 0)) &&
   if (($result < 2)); then
      echo Only one word matched
   else
      echo Both words matched
   fi

एक-लाइनर:

(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched

यदि आप पैटर्न को हार्ड-कोड नहीं करना चाहते हैं, तो तत्वों के एक चर सेट के साथ इसे एक फ़ंक्शन के साथ स्वचालित किया जा सकता है।

यह मूल रूप से बश में पाइप या अतिरिक्त प्रक्रियाओं के बिना एक फ़ंक्शन के रूप में किया जा सकता है, लेकिन अधिक शामिल होगा और संभवतः आपके प्रश्न के दायरे से बाहर है।


(१) मैं सोच रहा था कि कोई व्यक्ति पर्ल के नियमित एक्सप्रेशन का उपयोग करके कोई जवाब देने वाला है। यदि आपने अपनी पोस्ट के उस हिस्से पर ध्यान केंद्रित किया है, और बताया कि यह कैसे काम करता है, तो यह एक अच्छा जवाब हो सकता है। (२) लेकिन मुझे डर है कि बाकी सब इतना अच्छा नहीं है। प्रश्न कहता है "केवल दो शब्दों में से किसी एक को दर्शाने वाली लाइनें " (जोर दिया गया)। यदि आउटपुट लाइनों को माना जाता है , तो यह इस कारण से खड़ा होता है कि इनपुट भी कई लाइनों का होना चाहिए   लेकिन अपने दृष्टिकोण से काम करता है केवल जब केवल एक ही लाइन पर देख रहे हैं। ... (Cont'd)
G-Man ने कहा कि मोनिका '

(Cont'd)… उदाहरण के लिए, यदि इनपुट में लाइनें हैं Big apple\nऔर pear-shaped\n, तो आउटपुट में उन दोनों लाइनें शामिल होनी चाहिए। आपके समाधान को 2 की गिनती मिलेगी; लंबे संस्करण की रिपोर्ट "दोनों शब्दों से मेल खाती है" (जो कि गलत प्रश्न का उत्तर है) और लघु संस्करण कुछ भी नहीं कहेंगे। (३) एक सुझाव: -oयहां उपयोग करना वास्तव में एक बुरा विचार है, क्योंकि यह उन रेखाओं को छिपाता है जिनमें मैच होते हैं, इसलिए आप यह नहीं देख सकते हैं कि दोनों शब्द एक ही लाइन पर दिखाई देते हैं। ... (Cont'd)
G-Man ने कहा कि मोनिका '

(जारी) ... (4) निष्कर्ष: किसी के आपके उपयोग uniq/ sort -uऔर फैंसी पर्ल नियमित अभिव्यक्ति वास्तव में इस सवाल का जवाब एक उपयोगी तक जोड़ नहीं है प्रत्येक पंक्ति पर केवल पिछले घटना मैच के लिए। लेकिन, अगर उन्होंने ऐसा किया, तब भी यह एक बुरा जवाब होगा क्योंकि आप यह नहीं समझाते हैं कि वे सवाल का जवाब देने में कैसे योगदान करते हैं। ( एक अच्छी व्याख्या के उदाहरण के लिए स्टीफन चेज़ेलस का उत्तर देखें ।)
जी-मैन कहते हैं, 'मोनिका की बहाली'

ओपी का कहना है कि वे "केवल दो शब्दों में से किसी एक को दर्शाने वाली रेखाएँ" दिखाना चाहते थे, जिसका अर्थ है कि प्रत्येक पंक्ति का मूल्यांकन स्वयं किया जाना है। मैं नहीं देखता कि आपको ऐसा क्यों लगता है कि यह सवाल का जवाब नहीं देता है। कृपया एक उदाहरण इनपुट प्रदान करें जो आपको लगता है कि विफल हो जाएगा।
ज़ीरो

ओह, है कि आप क्या मतलब? “इनपुट को एक बार में पढ़ें और हर लाइन के लिए इन दो या तीन कमांड को निष्पादित करें "? (१) यह स्पष्ट रूप से अस्पष्ट है कि आपका क्या मतलब है। (२) यह दर्द रहित है। आपके पहले के चार जवाबों से पता चला कि कुछ कमांड (एक, दो या चार) में पूरी फाइल को कैसे संभालना है , और आप इनपुट की n लाइनों के लिए 3 × n कमांड चलाना चाहते हैं  ? यहां तक ​​कि अगर यह काम करता है, तो यह अनावश्यक रूप से महंगी निष्पादन के लिए एक डाउन वोट कमाता है। (3) बालों को विभाजित करने के जोखिम पर, यह अभी भी उपयुक्त लाइनों को दिखाने का काम नहीं करता है ।
जी-मैन का कहना है कि 'मोनिका' की बहाली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.