Jak uruchomić grep z wieloma wzorcami AND?

86

Chciałbym uzyskać dopasowanie wielu wzorców z niejawnym AND między wzorami, tj. Równoważne z uruchomieniem kilku greps w sekwencji:

grep pattern1 | grep pattern2 | ...

Jak więc przekonwertować to na coś takiego?

grep pattern1 & pattern2 & pattern3

Chciałbym użyć pojedynczego grepa, ponieważ dynamicznie buduję argumenty, więc wszystko musi mieścić się w jednym ciągu. Użycie filtru jest funkcją systemową, a nie grep, więc nie jest to argumentem.


Nie myl tego pytania z:

grep "pattern1\|pattern2\|..."

To jest dopasowanie wielu wzorców LUB .

Greenoldman
źródło
Podobne pytanie dotyczące SO: Sprawdź, czy w pliku istnieje wiele ciągów lub
wyrażeń regularnych

Odpowiedzi:

78

agrep można to zrobić za pomocą tej składni:

agrep 'pattern1;pattern2'

Dzięki GNU grep, gdy jest zbudowany z obsługą PCRE, możesz:

grep -P '^(?=.*pattern1)(?=.*pattern2)'

Z astgrep :

grep -X '.*pattern1.*&.*pattern2.*'

(dodanie .*s jako <x>&<y>dopasowuje ciągi, które pasują do obu <x>i <y> dokładnie , a&bnigdy nie będą pasować, ponieważ nie ma takiego ciągu, który może być jednocześnie ai bjednocześnie)

Jeśli wzorce się nie nakładają, możesz również wykonać następujące czynności:

grep -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

Najlepszym przenośnym sposobem jest prawdopodobnie awkjak już wspomniano:

awk '/pattern1/ && /pattern2/'

Z sed:

sed -e '/pattern1/!d' -e '/pattern2/!d'

Uwaga: wszystkie będą miały różną składnię wyrażeń regularnych.

Stéphane Chazelas
źródło
1
agrepSkładnia nie działa dla mnie ... która wersja została wprowadzona w?
Raman
@Raman 2.04 z 1992 roku już to miał. Nie mam powodu sądzić, że od początku jej nie było. Nowsze (po 1992) wersje agrepmożna znaleźć w glimpse / webglimpse . Być może masz inną implementację. Miałem błąd dla wersji AST-grep chociaż opcja dla wzmożone wyrażeniach regularnych jest -X, nie -A.
Stéphane Chazelas,
@ StéphaneChazelas Dzięki, mam agrepwersję 0.8.0 na Fedorze 23. Wygląda to inaczej agrepniż ta, do której się odwołujesz.
Raman,
1
@Raman, twoje brzmi jak TREagrep .
Stéphane Chazelas
2
@Techiee, lub po prostuawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas
19

Nie podałeś wersji grep, to ważne. Niektóre silniki wyrażeń regularnych umożliwiają wielokrotne dopasowanie pogrupowane według AND za pomocą „&”, ale jest to niestandardowa i nieprzenośna funkcja. Ale przynajmniej GNU grep tego nie obsługuje.

OTOH możesz po prostu zastąpić grep sed, awk, perl itp. (Wymienione w kolejności rosnącej masy). W przypadku awk polecenie wyglądałoby tak

awk '/ regexp1 / && / regexp2 / && / regexp3 / {print; } ”

i można go łatwo skonfigurować w wierszu poleceń.

Netch
źródło
3
Pamiętaj tylko, że awkużywa ERE, np. Odpowiednika grep -E, w przeciwieństwie do BRE, które grepużywa zwykły .
jw013,
3
awkWyrazy regularne nazywane są ERE, ale w rzeczywistości są nieco idiosynkratyczne. Oto prawdopodobnie więcej szczegółów, niż ktokolwiek się troszczy: wiki.alpinelinux.org/wiki/Regex
dubiousjim
Dziękuję, grep 2.7.3 (openSUSE). Głosowałem za tobą, ale przez pewien czas będę pozostawać otwartym pytaniem, może jest jakiś sposób na grep (nie, że nie lubię awk- po prostu wiedząc, że im więcej, tym lepiej).
greenoldman
2
Domyślną czynnością jest wydrukowanie pasującej linii, aby { print; }część nie była tak naprawdę potrzebna ani przydatna.
tripleee
7

Jeśli patternszawiera jeden wzór w wierszu, możesz zrobić coś takiego:

awk 'NR==FNR{a[$0];next}{for(i in a)if($0!~i)next}1' patterns -

Lub dopasowuje podciągi zamiast wyrażeń regularnych:

awk 'NR==FNR{a[$0];next}{for(i in a)if(!index($0,i))next}1' patterns -

Aby wydrukować wszystkie zamiast bez linii na wejściu w przypadku, gdy patternsjest pusta, wymień NR==FNRsię FILENAME==ARGV[1]lub z ARGIND==1w gawk.

Funkcje te drukują wiersze STDIN, które zawierają każdy ciąg znaków określony jako argument jako podłańcuch. gaoznacza grep all i gaiignoruje wielkość liter.

ga(){ awk 'FILENAME==ARGV[1]{a[$0];next}{for(i in a)if(!index($0,i))next}1' <(printf %s\\n "$@") -; }
gai(){ awk 'FILENAME==ARGV[1]{a[tolower($0)];next}{for(i in a)if(!index(tolower($0),i))next}1' <(printf %s\\n "$@") -; }
nisetama
źródło
7

To nie jest bardzo dobre rozwiązanie, ale ilustruje nieco fajną „sztuczkę”

function chained-grep {
    local pattern="$1"
    if [[ -z "$pattern" ]]; then
        cat
        return
    fi    

    shift
    grep -- "$pattern" | chained-grep "$@"
}

cat something | chained-grep all patterns must match order but matter dont
olejorgenb
źródło
1
Użyj jednej chained-grep()lub function chained-grepale nie function chained-grep(): unix.stackexchange.com/questions/73750/...
nisetama
3

git grep

Oto składnia przy użyciu git grepłączenia wielu wzorców przy użyciu wyrażeń logicznych :

git grep --no-index -e pattern1 --and -e pattern2 --and -e pattern3

Powyższe polecenie wypisze linie pasujące do wszystkich wzorów jednocześnie.

--no-index Wyszukaj pliki w bieżącym katalogu, który nie jest zarządzany przez Git.

Sprawdź man git-greppomoc.

Zobacz też:

Dla operacji LUB zobacz:

kenorb
źródło
1

ripgrep

Oto przykład z użyciem rg:

rg -N '(?P<p1>.*pattern1.*)(?P<p2>.*pattern2.*)(?P<p3>.*pattern3.*)' file.txt

Jest to jedno z najszybszych narzędzi greppingowych, ponieważ zostało zbudowane na silniku regularnym Rust, który wykorzystuje skończone automaty, SIMD i agresywne optymalizacje dosłowne, aby wyszukiwanie było bardzo szybkie.

Zobacz także powiązane żądanie funkcji w GH-875 .

kenorb
źródło
1

Oto moje zdanie, a to działa na słowa w wielu wierszach:

Użyj, find . -type fa następnie tyle
-exec grep -q 'first_word' {} \;
i ostatnie słowo kluczowe z
-exec grep -l 'nth_word' {} \;

-q
-lpliki pokazów cichych / cichych z dopasowaniem

Poniższa lista zwraca nazwy plików zawierające słowa „królik” i „dziura”:
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;

StackRover
źródło
-2

Aby znaleźć WSZYSTKIE słowa (lub wzory), możesz uruchomić grep w pętli FOR . Główną zaletą jest tutaj wyszukiwanie z listy wyrażeń regularnych .

EDYTUJ moją odpowiedź na prawdziwym przykładzie:

# search_all_regex_and_error_if_missing.sh 

find_list="\
^a+$ \
^b+$ \
^h+$ \
^d+$ \
"

for item in $find_list; do
   if grep -E "$item" file_to_search_within.txt 
   then
       echo "$item found in file."
   else
       echo "Error: $item not found in file. Exiting!"
       exit 1
   fi
done

Teraz uruchommy go na tym pliku:

hhhhhhhhhh

aaaaaaa

bbbbbbbbb

ababbabaabbaaa

ccccccc

dsfsdf

bbbb

cccdd

aa

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ znaleziono w pliku.

bbbbbbbbb bbbb

^ b + $ znaleziono w pliku.

hhhhhhhhhh

^ h + $ znaleziono w pliku.

Błąd: w pliku nie znaleziono ^ d + $. Exiting!

Noam Manos
źródło
1
Twoja logika jest wadliwa - poprosiłem o ALLoperatora, twój kod działa jak ORoperator, a nie AND. I btw. do tego ( OR) jest o wiele łatwiejsze rozwiązanie podane bezpośrednio w pytaniu.
greenoldman
@greenoldman Logika jest prosta: for zapętli WSZYSTKIE słowa / wzory na liście, a jeśli znajdzie się w pliku - wydrukuje go. Więc po prostu usuń pozostałe, jeśli nie potrzebujesz działania na wypadek, gdyby słowo nie zostało znalezione.
Noam Manos,
1
Rozumiem twoją logikę, a także moje pytanie - pytałem o ANDoperatora, co oznacza, że ​​plik jest tylko pozytywnym trafieniem, jeśli pasuje do wzoru A i wzoru B i wzoru C i ... ANDW twoim przypadku plik jest pozytywny, jeśli pasuje wzór A lub wzór B lub ... Czy widzisz teraz różnicę?
greenoldman
@ Greenoldman nie wiesz, dlaczego uważasz, że ta pętla nie sprawdza ORAZ warunek dla wszystkich wzorów? Więc zedytowałem swoją odpowiedź na prawdziwym przykładzie: przeszuka w pliku wszystkie wyrażenia regularne listy, a na pierwszym, którego brakuje - zakończy się z błędem.
Noam Manos,
Masz go przed oczami, masz pozytywny wynik zaraz po wykonaniu pierwszego meczu. Powinieneś „zebrać” wszystkie wyniki i obliczyć ANDje. Następnie powinieneś przepisać skrypt, aby działał na wielu plikach - wtedy może zdajesz sobie sprawę, że odpowiedź na to pytanie jest już gotowa, a próba nie przynosi niczego do stołu, przepraszam.
greenoldman