Grep के साथ एक लाइन में दो तारों का मिलान करें


218

मैं उन grepलाइनों का उपयोग करने की कोशिश कर रहा हूं जिनमें दो अलग-अलग तार हैं। मैंने निम्नलिखित की कोशिश की है, लेकिन यह उन रेखाओं से मेल खाता है जिनमें या तो string1 या string2 है जो मुझे नहीं चाहिए।

grep 'string1\|string2' filename

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


जवाबों:


189

आप उपयोग कर सकते हैं grep 'string1' filename | grep 'string2'

या, grep 'string1.*string2\|string2.*string1' filename


5
@AlexanderN वास्तव में मैं यह बहु के साथ काम नहीं कर सकता, इतना अजीब है कि इसे स्वीकार किया गया था ..
कुंभ राशि पॉवर

1
यह एक बहुस्तरीय सवाल नहीं था। यदि यह बहुस्तरीय था, grep -P पर्ल स्टाइल रेगेक्स का समर्थन करता है ...
स्कॉट प्रिव

20
केवल तभी काम करता है जब 'string1' और 'string2' दोनों एक ही लाइन पर हों। यदि आप 'string1' या 'string2' के साथ लाइनें ढूंढना चाहते हैं, तो user45949 का उत्तर देखें।
जीवनसंग ०१०

10
पहला विकल्प: एक grep को एक दूसरे में पाइप करना एक OR परिणाम नहीं देता है और यह एक AND परिणाम उत्पन्न करता है।
मसुकोमी

1
मैंने इस्तेमाल कियाgrep -e "string1" -e "string2"
रवि धोरिया

198

मुझे लगता है कि यह वही है जो आप देख रहे थे:

grep -E "string1|string2" filename

मुझे लगता है कि इस तरह से जवाब:

grep 'string1.*string2\|string2.*string1' filename

केवल उस मामले से मेल खाते हैं जहां दोनों मौजूद हैं, एक या दूसरे या दोनों नहीं।


14
grep -e "string1" -e "string2" filenameऐसा ही नहीं होगा ?
जोंसडिवेनी

25
यह कैसे string1 या string2 के लिए grep है। प्रश्न स्पष्ट रूप से बताता है कि वे string1 और string2 की तलाश कर रहे हैं।
ओरियन एल्जेनिल

9
बहुत यकीन है कि सवाल बहुत सटीक है:How do I match lines that contains *both* strings?
r0estir0bbe

क्या यह एक ही लाइन से प्रिंट कर सकता है?
吴毅 吴毅

1
यह उत्तर अभी भी यहाँ क्यों है? यह सवाल का जवाब नहीं है।
प्रोमेथियस

26

कहीं भी किसी भी क्रम में सभी शब्दों वाली फ़ाइलों की खोज करने के लिए:

grep -ril \'action\' | xargs grep -il \'model\' | xargs grep -il \'view_type\'

पहला grep एक पुनरावर्ती खोज ( r), मामले की अनदेखी ( i) और लिस्टिंग (प्रिंट आउट) को बंद कर देता है जो कि फाइल के नाम ( l) एक शब्द के लिए ( 'action'एकल उद्धरण के साथ ) मेल कर रहे हैं ।

इसके बाद के greps दूसरे शब्दों के लिए खोज करते हैं, केस की असंवेदनशीलता को बरकरार रखते हुए और मिलान वाली फाइलों को सूचीबद्ध करते हैं।

फ़ाइलों की अंतिम सूची जो आपको मिलेगी उनमें ये शब्द हैं, जो फ़ाइल में कहीं भी किसी भी क्रम में हैं।


2
माना! मैं बस ध्यान देता हूं कि मुझे xargs को रिक्त स्थान के साथ फ़ाइल नाम को संभालने के लिए एक "-d '\ n" देना था। यह मेरे लिए लिनक्स पर काम करता था: grep -ril 'foo' | xargs -d '\n' grep -il 'bar'
टॉमी हैरिस

16

यदि आपके पास एक सीमित रेगेक्स के विकल्प के grepसाथ है , तो आप उपयोग कर सकते हैं-Pperl

grep -P '(?=.*string1)(?=.*string2)'

जिसमें अतिव्यापी तार के साथ काम करने का लाभ है। यह का उपयोग कर कुछ और अधिक सीधा है perlके रूप में grepक्योंकि आप तर्क और सीधे निर्दिष्ट और कर सकते हैं:

perl -ne 'print if /string1/ && /string2/'

1
सबसे बढ़िया उत्तर। शेल बहुत आसान और त्वरित है, लेकिन एक बार जब पैटर्न जटिल हो जाता है तो आपको पायथन या पर्ल (या ऑक) का उपयोग करना चाहिए। यह साबित करने की कोशिश कर रही दीवार के खिलाफ अपने सिर को मत मारो यह शुद्ध शेल (जो भी इन दिनों का मतलब है) में किया जा सकता है। एक अनुस्मारक लोग, इन उपकरणों का उपयोग "एक लाइनर" सिंटैक्स में किया जा सकता है जो एक मौजूदा शेल स्क्रिप्ट में एम्बेड करने योग्य हैं।
स्कॉट प्रिव

12

आपकी विधि लगभग अच्छी थी, केवल -w गायब थी

grep -w 'string1\|string2' filename

1
कम से कम OS-X और FreeBSD पर यह काम करता है! मेरा अनुमान है कि आप किसी और चीज़ पर हैं (जिसे ओपी ने परिभाषित नहीं किया है - आशा है कि आपने आपके अलावा कई उपयोगकर्ताओं के लिए एक सही उत्तर नहीं दिया है)।
सिंह

मैं ओएस-एक्स पर हूं। शायद मैं यह सही ढंग से नहीं कर रहा हूं? मैंने जो किया, उस पर एक नज़र डालें: i.imgur.com/PFVlVAG.png
एरियल

1
अजीब। मुझे उम्मीद थी कि अंतर फाइल में नहीं है, लेकिन, अगर मैं अपने एलएस के साथ मेरी विधि को पाइप करता हूं, तो मुझे परिणाम मिलता है कि आप नहीं: imgur.com/8eTt3Ak.png - दोनों ओएस-एक्स 10.9.5 पर दोनों "grep (BSD grep) 2.5.1-FreeBSD") और FreeBSD 10 ("grep (GNU grep) 2.5.1-FreeBSD")। मैं उत्सुक हूं कि आपका क्या grep -Vहै।
सिंह

1
आपके उदाहरण मेरे लिए काम कर रहे हैं: i.imgur.com/K8LM69O.png तो अंतर यह है कि इस पद्धति से सबस्ट्रिंग नहीं होते हैं, उन्हें अपने दम पर पूरा होना होगा। मुझे लगता है कि आपको सबस्ट्रिंग की खोज के लिए grep के भीतर regexps का निर्माण करना होगा। कुछ इस तरह से:grep -w 'regexp1\|regexp2' filename
एरियल

2
OP, string1 या string2 का मिलान करके एक उदाहरण दिखाता है और पूछता है कि उन रेखाओं का मिलान कैसे करें जिनमें दोनों तार शामिल हैं। यह उदाहरण अभी भी पैदावार या।
gustafbstrom

7

|एक नियमित अभिव्यक्ति में ऑपरेटर का मतलब है या। यह कहना है कि या तो string1 या string2 मेल खाएगा। तुम यह कर सकते थे:

grep 'string1' filename | grep 'string2'

जो पहले कमांड से दूसरे grep में परिणाम देगा। कि आप केवल लाइनों है कि दोनों से मेल खाना चाहिए।


1
आपके कथन सत्य हैं, लेकिन ओपी प्रश्न का उत्तर नहीं देते
बेन व्हीलर

यह सवाल का जवाब देता है और यह वास्तव में है कि ज्यादातर लोग इसे कैसे लिखते हैं।
पीटर के

7

आप कुछ इस तरह की कोशिश कर सकते हैं:

(pattern1.*pattern2|pattern2.*pattern1)

4

और जैसा कि लोगों ने पर्ल और अजगर, और दृढ़ शैल लिपियों का सुझाव दिया है, यहाँ एक सरल जाग दृष्टिकोण है:

awk '/string1/ && /string2/' filename

स्वीकृत उत्तर के लिए टिप्पणियों को देखा: नहीं, यह बहु-पंक्ति नहीं करता है; लेकिन फिर यह भी नहीं है कि प्रश्न के लेखक ने क्या पूछा।


3

इसके लिए grep का उपयोग करने का प्रयास न करें, इसके बजाय awk का उपयोग करें। 2 regexps R1 और R2 को grep में मिलाने के लिए आपको लगता है कि यह होगा:

grep 'R1.*R2|R2.*R1'

जागते समय यह होगा:

awk '/R1/ && /R2/'

लेकिन क्या होगा अगर R2एक सबसेट के साथ या ओवरलैप हो जाए R1? उस grep कमांड बस काम नहीं करेगा जबकि awk कमांड होगा। कहते हैं कि आप उन पंक्तियों को खोजना चाहते हैं जिनमें शामिल हैं theऔर heat:

$ echo 'theatre' | grep 'the.*heat|heat.*the'
$ echo 'theatre' | awk '/the/ && /heat/'
theatre

आपको इसके लिए 2 greps और एक पाइप का उपयोग करना होगा:

$ echo 'theatre' | grep 'the' | grep 'heat'
theatre

और निश्चित रूप से अगर आपने वास्तव में उन्हें अलग करने की आवश्यकता की थी, तो आप हमेशा उसी regexp को awk में लिख सकते हैं जैसा कि आपने grep में उपयोग किया था और वैकल्पिक awk समाधान हैं जो regexps को हर संभव अनुक्रम में दोहराना शामिल नहीं करते हैं।

इसे एक तरफ रखते हुए, क्या होगा यदि आप 3 रेगेक्स आर 1, आर 2, और आर 3 से मेल खाने के लिए अपने समाधान का विस्तार करना चाहते हैं। इन गरीब विकल्पों में से एक grep में:

grep 'R1.*R2.*R3|R1.*R3.*R2|R2.*R1.*R3|R2.*R3.*R1|R3.*R1.*R2|R3.*R2.*R1' file
grep R1 file | grep R2 | grep R3

जागते समय यह संक्षिप्त, स्पष्ट, सरल, कुशल होगा:

awk '/R1/ && /R2/ && /R3/'

अब, क्या होगा यदि आप वास्तव में regexps R1 और R2 के बजाय शाब्दिक तार S1 और S2 से मेल खाना चाहते हैं? आप बस ऐसा नहीं कर सकते हैं कि एक कॉल में grep करने के लिए, आपको कॉल करने से पहले सभी RE मेटाचर्स से बचने के लिए कोड लिखना होगा:

S1=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R1')
S2=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R2')
grep 'S1.*S2|S2.*S1'

या फिर 2 greps और एक पाइप का उपयोग करें:

grep -F 'S1' file | grep -F 'S2'

जो फिर से खराब विकल्प हैं जबकि awk के साथ आप बस regexp ऑपरेटर के बजाय एक स्ट्रिंग ऑपरेटर का उपयोग करते हैं:

awk 'index($0,S1) && index($0.S2)'

अब, क्या होगा अगर आप एक पंक्ति के बजाय एक पैराग्राफ में 2 रेगेक्स से मिलान करना चाहते हैं? Grep में नहीं किया जा सकता, awk में तुच्छ:

awk -v RS='' '/R1/ && /R2/'

कैसे एक पूरी फ़ाइल के बारे में? फिर से awk में grep और तुच्छ नहीं किया जा सकता है (इस समय मैं GNU awk का उपयोग मल्टी-चार RS के लिए संक्षिप्तता के लिए कर रहा हूं, लेकिन यह किसी भी awk में अधिक कोड नहीं है या आप नियंत्रण-चार चुन सकते हैं जिसे आप नहीं जानते हैं रुपये के लिए इनपुट में ही हो)

awk -v RS='^$' '/R1/ && /R2/'

इसलिए - यदि आप एक पंक्ति या पैराग्राफ या फ़ाइल में कई regexps या तार ढूंढना चाहते हैं तो grep का उपयोग न करें, awk का उपयोग करें।


है awk '/R1/ && /R2/'केस-संवेदी?
प्रोमेथियस

@ हाशिम - नहीं। GNU awk के साथ इसे असंवेदनशील बनाने के लिए आप awk -v IGNORECASE=1 '/R1/ && /R2/'किसी भी जागरण के साथawk '{x=toupper($0)} x~/R1/ && x~/R2/'
Ed Morton


2

ऐसी लाइनें मिलीं जो केवल 6 स्थानों से शुरू होती हैं और उनके साथ समाप्त होती हैं:

 cat my_file.txt | grep
 -e '^      .*(\.c$|\.cpp$|\.h$|\.log$|\.out$)' # .c or .cpp or .h or .log or .out
 -e '^      .*[0-9]\{5,9\}$' # numers between 5 and 9 digist
 > nolog.txt

2

मान लें कि हमें एक फ़ाइल टेस्टफाइल में कई शब्दों की गिनती खोजने की आवश्यकता है। इसके बारे में जाने के दो तरीके हैं

1) regex मिलान पैटर्न के साथ grep कमांड का उपयोग करें

grep -c '\<\(DOG\|CAT\)\>' testfile

2) egrep कमांड का उपयोग करें

egrep -c 'DOG|CAT' testfile 

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


2

git grep

यहाँ git grepकई पैटर्न के साथ वाक्य रचना है :

git grep --all-match --no-index -l -e string1 -e string2 -e string3 file

आप बूलियन अभिव्यक्तियों जैसे --and, --orऔर के साथ पैटर्न भी जोड़ सकते हैं --not

man git-grepमदद के लिए जाँच करें ।


--all-matchकई पैटर्न अभिव्यक्तियाँ देते समय, यह ध्वज मेल को उन फ़ाइलों तक सीमित करने के लिए निर्दिष्ट किया जाता है, जिनमें उन सभी को मिलाने के लिए लाइनें होती हैं

--no-index वर्तमान निर्देशिका में फ़ाइलें खोजें जो Git द्वारा प्रबंधित नहीं हैं।

-l/ --files-with-matches/ --name-onlyकेवल फाइलों के नाम दिखाएं।

-eअगला पैरामीटर पैटर्न है। मूलभूत रीएजएक्सपीपी का उपयोग करना डिफ़ॉल्ट है।

विचार करने के लिए अन्य परम:

--threads उपयोग करने के लिए grep कार्यकर्ता थ्रेड्स की संख्या।

-q/ --quiet/ --silentआउटपुट लाइनों से मेल नहीं खाता; स्टेटस 0 से बाहर निकलें जब कोई मैच हो।

पैटर्न प्रकार बदलने के लिए, आप भी उपयोग कर सकते हैं -G/ --basic-regexp(डिफ़ॉल्ट), -F/ --fixed-strings, -E/ --extended-regexp, -P/ --perl-regexp, -f fileऔर अन्य।

सम्बंधित:

के लिए या आपरेशन, देखें:


2
हमेशा सोचा था कि "git grep" केवल एक git रिपॉजिटरी के अंदर चलाया जा सकता है। मुझे -नो-इंडेक्स विकल्प के बारे में पता नहीं था। इस पर ध्यान दिलाने के लिए धन्यवाद!
कामराजु कुसुमंची

1

उस स्ट्रिंग्स को रखें जिसे आप फ़ाइल के लिए तैयार करना चाहते हैं

echo who    > find.txt
echo Roger >> find.txt
echo [44][0-9]{9,} >> find.txt

फिर -f का उपयोग करके खोजें

grep -f find.txt BIG_FILE_TO_SEARCH.txt 

1
grep '(string1.*string2 | string2.*string1)' filename

किसी भी क्रम में string1 और string2 के साथ लाइन मिलेगी


किस तरह से कम से कम शीर्ष दो उत्तरों से अलग है?
luk2302

1
grep -i -w 'string1\|string2' filename

यह सटीक शब्द मिलान और केस केस असंवेदनशील शब्दों के लिए काम करता है, इसके लिए -i का उपयोग किया जाता है


0

बहुस्तरीय मिलान के लिए:

echo -e "test1\ntest2\ntest3" |tr -d '\n' |grep "test1.*test3"

या

echo -e "test1\ntest5\ntest3" >tst.txt
cat tst.txt |tr -d '\n' |grep "test1.*test3\|test3.*test1"

हमें सिर्फ न्यूलाइन वर्ण को हटाने की आवश्यकता है और यह काम करता है!


0

आपको grepइस तरह होना चाहिए :

$ grep 'string1' file | grep 'string2'

1
यह एक तार्किक और करता है। ओपी एक तार्किक या चाहता है।
बेन व्हीलर

1
@BenWheeler: इस सवाल से: "तो मैं grep के साथ कैसे मेल खाता हूं, जिसमें दोनों तार हैं?"
एरिक I

0

मैं अक्सर आपकी ही तरह समस्या में भागता हूं, और मैंने अभी स्क्रिप्ट का एक टुकड़ा लिखा है:

function m() { # m means 'multi pattern grep'

    function _usage() {
    echo "usage: COMMAND [-inH] -p<pattern1> -p<pattern2> <filename>"
    echo "-i : ignore case"
    echo "-n : show line number"
    echo "-H : show filename"
    echo "-h : show header"
    echo "-p : specify pattern"
    }

    declare -a patterns
    # it is important to declare OPTIND as local
    local ignorecase_flag  filename linum header_flag colon result OPTIND

    while getopts "iHhnp:" opt; do
    case $opt in
        i)
        ignorecase_flag=true ;;
        H)
        filename="FILENAME," ;;
        n)
        linum="NR," ;;
        p)
        patterns+=( "$OPTARG" ) ;;
        h)
        header_flag=true ;;
        \?)
        _usage
        return ;;
    esac
    done

    if [[ -n $filename || -n $linum ]]; then
    colon="\":\","
    fi

    shift $(( $OPTIND - 1 ))

    if [[ $ignorecase_flag == true ]]; then
    for s in "${patterns[@]}"; do
            result+=" && s~/${s,,}/"
    done
    result=${result# && }
    result="{s=tolower(\$0)} $result"
    else
    for s in "${patterns[@]}"; do
            result="$result && /$s/"
    done
    result=${result# && }
    fi

    result+=" { print "$filename$linum$colon"\$0 }"

    if [[ ! -t 0 ]]; then       # pipe case
    cat - | awk "${result}"
    else
    for f in "$@"; do
        [[ $header_flag == true ]] && echo "########## $f ##########"
        awk "${result}" $f
    done
    fi
}

उपयोग:

echo "a b c" | m -p A 
echo "a b c" | m -i -p A # a b c

आप चाहें तो इसे .bashrc में डाल सकते हैं।


0

जब दोनों तार क्रम में हों, तब grepकमांड पर बीच में एक पैटर्न लगाएं :

$ grep -E "string1(?.*)string2" file

उदाहरण के लिए, यदि निम्न पंक्तियाँ एक फ़ाइल में समाहित हैं, जिसका नाम है Dockerfile:

FROM python:3.8 as build-python
FROM python:3.8-slim

उस रेखा को प्राप्त करने के लिए जिसमें तार हैं: FROM pythonऔर as build-pythonफिर उपयोग करें:

$ grep -E "FROM python:(?.*) as build-python" Dockerfile

तब आउटपुट केवल उस पंक्ति को दिखाएगा जिसमें दोनों तार होते हैं :

FROM python:3.8 as build-python

-2

ripgrep

यहाँ उदाहरण का उपयोग कर रहा है rg:

rg -N '(?P<p1>.*string1.*)(?P<p2>.*string2.*)' file.txt

यह सबसे तेज ग्रेपिंग टूल में से एक है, क्योंकि यह रस्ट के रेगेक्स इंजन के ऊपर बनाया गया है , जो बहुत तेजी से खोज करने के लिए परिमित ऑटोमेटा, SIMD और आक्रामक शाब्दिक अनुकूलन का उपयोग करता है।

इसका उपयोग करें, खासकर जब आप एक बड़े डेटा के साथ काम कर रहे हों।

GH-875 पर संबंधित सुविधा अनुरोध भी देखें ।


1
यह उत्तर बिल्कुल सही नहीं है। नामित कैप्चरिंग समूह अनावश्यक हैं, और string2इससे पहले दिखाई देने पर मामला संभाल नहीं पाता है string1। इस समस्या का सबसे सरल समाधान है rg string1 file.txt | rg string2
BurntSushi5
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.