2010-12-20 13 views
143

Próbuję użyć grep, aby dopasować wiersze zawierające dwa różne ciągi. Próbowałem następujące, ale to dopasowuje linie, które zawierają albo string1lubstring2 które nie jest to, co chcę.Jak korzystać z grep, aby dopasować wiele ciągów w tej samej linii?

grep 'string1\|string2' filename 

Więc jak mogę się równać z grep tylko linie, które zawierają oba ciągi?

+1

Powiązane: https://unix.stackexchange.com/questions/37313/how-do-i- grep-for-multiple-patterns –

Odpowiedz

118

Można użyć grep 'string1' filename | grep 'string2'

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

+2

spróbuj z multilinią –

+3

@AlexanderN naprawdę nie mogę sprawić, żeby działało z multilinią, to jest tak dziwne, że zostało zaakceptowane .. –

+1

To nie było pytanie wielowierszowe. Jeśli byłby wielowierszowy, grep -P obsługuje wyrażenie regularne w stylu Perla ... –

3

Operator | w wyrażeniu regularnym oznacza lub. Oznacza to, że albo string1, albo string2 będą pasować. Możesz zrobić:

grep 'string1' filename | grep 'string2' 

, który przefaksuje wyniki z pierwszego polecenia do drugiego grepa. To powinno dać ci tylko linie, które pasują do obu.

+0

Twoje oświadczenia są prawdziwe, ale nie odpowiadają na pytanie OP –

7

Można spróbować czegoś takiego:

(pattern1.*pattern2|pattern2.*pattern1) 
0

Trzeba grep takiego:

$ grep 'string1' file | grep 'string2' 
+0

Wykonuje logiczną operację AND. OP chce logicznego OR. –

12

Jeśli masz a grep z -P rozwiązaniem dla ograniczonej perl regex, można użyć

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

który ma tę zaletę, że działa z nakładających strun. Jest to nieco prostsze przy użyciu perl jako grep, ponieważ można określić i logika bardziej bezpośrednio:

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

Najlepsza odpowiedź. Powłoka jest bardzo łatwa i szybka, ale gdy wzorzec się skomplikuje, powinieneś użyć Pythona lub Perla (lub Awk). Nie uderzaj głową o ścianę, próbując udowodnić, że można to zrobić w czystej skorupie (cokolwiek to znaczy w dzisiejszych czasach). Przypomnienie, narzędzia te mogą być używane w składni "jednego liniowca", który osadza dibble w istniejącym skrypcie powłoki. –

159

myślę, że to jest to, czego szukali:

grep -E "string1|string2" filename 

Myślę, że odpowiedzi tak:

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

pasuje tylko do przypadku, w którym oba są obecne, a nie jedno lub drugie lub oba.

+11

nie "grep -e" string1 "-e" string2 "filename" robi to samo? – janosdivenyi

+1

Również nazwa egrep 'string1 | string2 | string3' będzie działać. –

+12

to jak grep dla string1 LUB string2. pytanie jasno stwierdza, że ​​szukają ciągu1 i ciągu2. –

0

dla wielowierszowego meczu:

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

lub

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

musimy tylko usunąć znak nowej linii i to działa!

23

Aby wyszukać pliki zawierające wszystkie słowa w dowolnej kolejności wszędzie:

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

Pierwszy grep startuje rekurencyjne przeszukiwanie (r), ignorując sprawę (i) wystawianie (drukowanie) nazwę pliki, które pasują (l) na jeden termin ('action' z pojedynczymi cudzysłowami) występujące w dowolnym miejscu pliku.

Kolejne greps wyszukuje inne warunki, nie rozróżnia wielkości liter i wyświetla listę pasujących plików.

Ostateczna lista plików, które otrzymasz, zawiera te warunki, w dowolnej kolejności w dowolnym miejscu pliku.

+0

Zgoda! Po prostu zauważę, że musiałem dać xargs "-d" \ n "", aby obsługiwać nazwy plików ze spacjami. To działało dla mnie w Linuksie: 'grep -ril 'foo' | xargs -d '\ n' grep -il 'bar'' –

10

Twoja metoda było prawie dobrze, tylko brakuje -w

grep -w 'string1\|string2' filename 
+0

To nie działa! – Ariel

+1

Przynajmniej na OS X i FreeBSD to działa! Zgaduję, że jesteś na czymś innym (czego OP nie zdefiniował - mam nadzieję, że nie przesłałeś poprawnej odpowiedzi do wielu użytkowników z wyjątkiem ciebie). – Leo

+0

Jestem na OS-X. Być może nie robię tego poprawnie? Zobacz, co zrobiłem: http://i.imgur.com/PFVlVAG.png – Ariel

40

Wystarczy dać mu wiele -e opcje.

-e pattern, --regexp=pattern 
     Specify a pattern used during the search of the input: an input 
     line is selected if it matches any of the specified patterns. 
     This option is most useful when multiple -e options are used to 
     specify multiple patterns, or when a pattern begins with a dash 
     (`-'). 

W ten sposób staje się komenda:

grep -e "string1" -e "string2" filename 

Uwaga: Powyżej przytoczyłem podręcznik wersji BSD, ale wygląda na to, że to the same on Linux.

+11

Uwaga: to pasuje do _any_ z ciągów, a nie do obu, o które prosił OP. – gustafbstrom

0

Place struny chcesz grep do do pliku

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

Następnie wyszukać używając -f

grep -f find.txt BIG_FILE_TO_SEARCH.txt 
1

Znaleziono linie, które uruchamia się tylko z 6 pomieszczeń i wykończony:

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 
0
grep '(string1.*string2 | string2.*string1)' filename 

dostanie linię łańcuch1 i łańcuch2 w dowolnej kolejności

+0

W jaki sposób różni się od przynajmniej dwóch najlepszych odpowiedzi? – luk2302

0

często napotkasz ten sam problem jak ty, a ja po prostu napisał kawałek skryptu:

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 "[email protected]"; do 
     [[ $header_flag == true ]] && echo "########## $f ##########" 
     awk "${result}" $f 
    done 
    fi 
} 

Zastosowanie:

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

Możesz umieścić go w .bashrc, jeśli chcesz.

0

Powiedzmy, że musimy znaleźć liczbę wielu słów w pliku testowym pliku. Istnieją dwa sposoby, aby przejść o nim

1) Użyj polecenia grep z pasującym regex wzór

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

2) Korzystanie z egrep dowodzić

egrep -c 'DOG|CAT' testfile 

Z egrep nie musisz się martwić o wyrażeniu i po prostu oddziel słowa przez separator rur.

0

A jak ludzie sugerują, Perl i Python, i zawiłe skrypty powłoki, tutaj prosty awk podejście:

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

Spojrzeliśmy w komentarzach do przyjętej Odpowiedź: Nie, to nie robi wielu -linia; ale to też nie jest to, o co pytał autor pytania.

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

Działa to na dokładne dopasowanie słowo i dopasowywanie wielkość liter ma znaczenie słów, na które służy -i

Powiązane problemy