Grep birden fazla AND deseniyle nasıl çalıştırılır?


86

Çoklu desen eşleşmesini örtük AND ile örüntüler arasında eşleştirmek istiyorum , yani bir dizi içinde birkaç grep çalıştırmaya eşdeğer:

grep pattern1 | grep pattern2 | ...

Peki nasıl bir şeye dönüştürülür?

grep pattern1 & pattern2 & pattern3

Tekli grep kullanmak istiyorum çünkü dinamik olarak argümanlar yapıyorum, bu yüzden her şey bir dizgeye sığmalı. Filtrenin kullanılması sistem özelliğidir, grep değildir, dolayısıyla onun için bir argüman değildir.


Bu soruyu karıştırmayın:

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

Bu bir OR çoklu model eşleşmesidir.



Yanıtlar:


78

agrep bu sözdizimi ile yapabilirsiniz:

agrep 'pattern1;pattern2'

GNU grepile PCRE desteğiyle oluşturulduğunda şunları yapabilirsiniz:

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

İle astgrep :

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

(ekleme .*s olarak <x>&<y>hem maç dizeleri maçları <x>ve <y> tam olarak , a&bhiçbir tür dizesinde olduğu gibi maç asla olmak hem ave baynı zamanda).

Desenler üst üste gelmiyorsa, şunları da yapabilirsiniz:

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

En iyi taşınabilir yol muhtemelen awkdaha önce belirtildiği gibi.

awk '/pattern1/ && /pattern2/'

İle sed:

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

Lütfen bunların hepsinin düzenli ifade sözdizimine sahip olacağını unutmayın.


1
agrepSözdizimi hangi sürümü tanıtıldı oldu ... benim için çalışmıyor?
Raman

1992'den @Raman 2.04 zaten sahipti. En başından beri orada olmadığına inanmak için hiçbir nedenim yok. Daha yeni (1992'den sonra) sürümleri belirti / belirtiagrep ile birlikte bulunabilir . Muhtemelen farklı bir uygulamanız var. Ben seçeneği olsa ast-grep sürümü için bir hata vardı artırılmış regexp'ler olduğunu -Xdeğil -A.
Stéphane Chazelas

@ StéphaneChazelas Teşekkürler, agrepFedora 23'te 0.8.0 var . Bu agrep, referans gösterdiğinizden farklı görünüyor .
Raman

1
@Raman, seninki gibi geliyor TREagrep .
Stéphane Chazelas

2
@Techiee, ya da sadeceawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas

19

Grep sürümünü belirtmediniz, bu önemlidir. Bazı regexp motorları '&' kullanarak VE ile gruplanan çoklu eşleşmeye izin verir, ancak bu standart değildir ve taşınabilir değildir. Ancak, en azından GNU grep bunu desteklemiyor.

OTOH grep'i sed, awk, perl, vb. İle değiştirebilirsiniz (ağırlık artış sırasına göre listelenmiştir). Awk ile komut şöyle görünür

awk '/ regexp1 / && / regexp2 / && / regexp3 / {yazdırma; }'

ve komut satırında kolay şekilde belirtilebilecek şekilde oluşturulabilir.


3
Sadece awkERE'leri, örneğin grep -Eovaların grepkullandığı BRE'lerin aksine , örneğin eşdeğerlerini kullandığını unutmayın.
jw013

3
awk'nin regex'lerine ERE denir , fakat aslında biraz kendine özgüdürler. Muhtemelen herkesin umurunda olduğundan daha fazla ayrıntı vardır: wiki.alpinelinux.org/wiki/Regex
dubiousjim

Teşekkürler, grep 2.7.3 (openSUSE). Seni kızdırdım ama bir süre soruyu açık tutacağım, belki grep için bazı hileler var (sevmediğim değil awk- sadece daha fazlasını bilmek iyidir).
greenoldman

2
Varsayılan işlem, eşleşen satırı yazdırmaktır, böylece { print; }parça burada gerçekten gerekli veya kullanışlı değildir.
üçlü

7

Eğer patternssatır başına bir deseni içerir, böyle bir şey yapabilirsiniz:

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

Veya bu normal ifadeler yerine alt dizelerle eşleşir:

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

Yerine durumda girdi hiçbir hatlarının tüm yazdırmak için patternsboş yerine NR==FNRbirlikte FILENAME==ARGV[1]veya birlikte ARGIND==1içinde gawk.

Bu işlevler, alt dize olarak bir argüman olarak belirtilen her dizeyi içeren STDIN satırlarını yazdırır. gagrep'in kısaltması ve gaidavayı yok sayar.

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 "$@") -; }

7

Bu çok iyi bir çözüm değil ama biraz havalı bir "hile" gösteriyor

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

1
İkisinden birini kullanın chained-grep()veya function chained-grepolmasın function chained-grep(): unix.stackexchange.com/questions/73750/…
nisetama

3

git grep

Boolean ifadeleri git grepkullanarak çoklu kalıpları birleştirmenin kullanıldığı sözdizimi :

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

Yukarıdaki komut bir kerede tüm kalıplara uyan satırları basacaktır.

--no-index Git tarafından yönetilmeyen geçerli dizindeki dosyaları arayın.

man git-grepYardım için kontrol edin .

Ayrıca bakınız:

İçin YA operasyon, bkz:


1

ripgrep

İşte kullanarak örnek rg:

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

En hızlı grepping araçlarından biridir, çünkü sonlu otomatlar, SIMD ve aramaları çok hızlı hale getirmek için agresif değişmez optimizasyonlar kullanan Rust'un regex motorunun üzerine inşa edilmiştir .

Ayrıca GH-875'deki ilgili özellik talebine de bakın .


1

İşte benim aldığım ve bu birden fazla satırdaki kelimeler için çalışıyor:

Kullanın find . -type fardından
-exec grep -q 'first_word' {} \;
en son ve en son anahtar kelimeyi kullanın.
-exec grep -l 'nth_word' {} \;

-qEşleşen sessiz / sessiz
-lgösteri dosyaları

Aşağıdakiler 'tavşan' ve 'delik' kelimelerinin bulunduğu dosya adlarının listesini döndürür:
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;


-2

Kelimelerin TÜMÜNÜ (veya kalıpları) bulmak için, grep'i FOR döngüsünde çalıştırabilirsiniz . Buradaki en büyük avantaj, bir regex listesinden arama yapmaktır .

DÜZENLEME gerçek örnekle cevabımı:

# 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

Şimdi bu dosyada çalıştıralım:

hhhhhhhhhh

aaaaaaa

bbbbbbbbb

ababbabaabbaaa

CCCCCCC

dsfsdf

bbbb

cccdd

aa

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ dosyasında bulundu.

bbbbbbbbb bbbb

^ b + $ dosyada bulundu.

hhhhhhhhhh

^ h + $ dosyada bulundu.

Hata: ^ d + $ dosyada bulunamadı. Çıkma!


1
Mantığınız hatalı - ALLOperatör istedim , kodunuz ORoperatör olarak çalışıyor , değil AND. Ve btw. Bunun için ( OR) soruda verilen çok daha kolay bir çözümdür.
greenoldman,

@greenoldman Mantık basittir: for for listedeki TÜM kelimelerde / kalıplarda döngü kurar ve dosyada bulunursa - basar . Yani kelimenin bulunmaması durumunda eyleme ihtiyacınız yoksa, sadece diğerini kaldırın.
Noam Manos,

1
Mantığınızı ve benim sorumu anlıyorum - ANDoperatör hakkında soruyorum , yani dosyanın yalnızca A ve B deseniyle ANDeşleşmesi durumunda pozitif bir isabet olması anlamına geliyordu. desen A veya desen B veya ... Farkı şimdi görüyor musunuz?
greenoldman

@greenoldman, bu döngünün neden tüm desenler için VE koşulunu kontrol etmediğini sandığınızdan emin değil mi? Bu yüzden cevabımı gerçek bir örnekle düzelttim: Listedeki tüm regex'leri dosyada arayacak ve eksik olan birincide - hatayla çıkacak.
Noam Manos

Tam gözünüzün önünde, ilk maçtan hemen sonra olumlu bir eşleşme var. Tüm sonuçları “toplamanız” ve hesaplamanız ANDgerekir. Ardından komut dosyasını birden fazla dosyada çalışacak şekilde yeniden yazmalısınız - o zaman belki sorunun çoktan cevaplandırıldığını ve girişimin masaya bir şey getirmediğini farkedersiniz, üzgünüm.
greenoldman
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.