Bagaimana menjalankan grep dengan banyak pola AND?

86

Saya ingin mendapatkan kecocokan multi pola dengan AND di antara pola, yaitu setara dengan menjalankan beberapa greps secara berurutan:

grep pattern1 | grep pattern2 | ...

Jadi bagaimana cara mengubahnya menjadi seperti ini?

grep pattern1 & pattern2 & pattern3

Saya ingin menggunakan grep tunggal karena saya membangun argumen secara dinamis, jadi semuanya harus masuk dalam satu string. Menggunakan filter adalah fitur sistem, bukan grep, jadi itu bukan argumen untuk itu.


Jangan bingung pertanyaan ini dengan:

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

Ini adalah pertandingan ATAU multi-pola.

Greenoldman
sumber

Jawaban:

79

agrep dapat melakukannya dengan sintaks ini:

agrep 'pattern1;pattern2'

Dengan GNU grep, ketika dibangun dengan dukungan PCRE, Anda dapat melakukan:

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

Dengan astgrep :

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

(menambahkan .*sebagai <x>&<y>cocok string yang cocok baik <x>dan <y> tepat , a&btidak akan pernah cocok karena tidak ada tali sehingga bisa menjadi baik adan bpada saat yang sama).

Jika polanya tidak tumpang tindih, Anda juga dapat melakukan:

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

Cara portabel terbaik mungkin dengan yang awktelah disebutkan:

awk '/pattern1/ && /pattern2/'

Dengan sed:

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

Harap berhati-hati bahwa semua itu akan memiliki sintaks ekspresi reguler yang berbeda.

Stéphane Chazelas
sumber
1
The agrepsintaks tidak bekerja untuk saya ... yang versi apakah itu diperkenalkan di?
Raman
@Raman 2.04 dari tahun 1992 sudah memilikinya. Saya tidak punya alasan untuk percaya itu tidak ada sejak awal. Versi yang lebih baru (setelah 1992) agrepdapat ditemukan disertakan dengan glimpse / webglimpse . Mungkin Anda memiliki implementasi yang berbeda. Saya memiliki kesalahan untuk versi ast-grep, opsi untuk augex regexps adalah -X, bukan -A.
Stéphane Chazelas
@ StéphaneChazelas Terima kasih, saya memiliki agrep0.8.0 di Fedora 23. Ini tampaknya berbeda agrepdari yang Anda referensi.
Raman
1
@Aman, suaramu terdengar seperti TREagrep .
Stéphane Chazelas
2
@ Techiee, atau hanyaawk '/p1/ && /p2/ {n++}; END {print 0+n}'
Stéphane Chazelas
19

Anda tidak menentukan versi grep, ini penting. Beberapa mesin regexp memungkinkan beberapa pencocokan dikelompokkan oleh DAN menggunakan '&' tetapi ini adalah fitur non-standar dan non-portabel. Tapi, setidaknya GNU grep tidak mendukung ini.

OTOH Anda cukup mengganti grep dengan sed, awk, perl, dll. (Tercantum dalam urutan kenaikan berat). Dengan awk, perintahnya akan terlihat seperti

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

dan dapat dibangun untuk ditentukan dalam baris perintah dengan cara yang mudah.

Netch
sumber
3
Ingatlah bahwa awkmenggunakan ERE's, misalnya yang setara grep -E, yang bertentangan dengan BRE yang biasa grepdigunakan.
jw013
3
awkRegex disebut EREs, tetapi sebenarnya mereka agak istimewa. Berikut ini mungkin lebih banyak detail daripada yang dipedulikan siapa pun: wiki.alpinelinux.org/wiki/Regex
dubiousjim
Terima kasih, grep 2.7.3 (openSUSE). Saya membesarkan hati Anda, tetapi saya akan membiarkan pertanyaan terbuka untuk sementara waktu, mungkin ada beberapa trik untuk grep (bukannya saya tidak suka awk- hanya mengetahui lebih banyak lebih baik).
greenoldman
2
Tindakan default adalah mencetak baris yang cocok sehingga { print; }bagian tersebut tidak benar-benar diperlukan atau berguna di sini.
tripleee
7

Jika patternsberisi satu pola per baris, Anda dapat melakukan sesuatu seperti ini:

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

Atau ini cocok dengan substring alih-alih ekspresi reguler:

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

Untuk mencetak semua bukan tidak ada garis input dalam kasus yang patternskosong, ganti NR==FNRdengan FILENAME==ARGV[1], atau dengan ARGIND==1di gawk.

Fungsi-fungsi ini mencetak garis-garis STDIN yang berisi setiap string yang ditentukan sebagai argumen sebagai substring. gasingkatan grep all dan gaiabaikan case.

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
sumber
7

Ini bukan solusi yang sangat baik tetapi menggambarkan "trik" yang agak keren

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
sumber
1
Gunakan salah satu chained-grep()atau function chained-greptidak function chained-grep(): unix.stackexchange.com/questions/73750/…
nisetama
3

git grep

Berikut adalah sintaks menggunakan git grepmenggabungkan beberapa pola menggunakan ekspresi Boolean :

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

Perintah di atas akan mencetak garis yang cocok dengan semua pola sekaligus.

--no-index Cari file di direktori saat ini yang tidak dikelola oleh Git.

Periksa man git-grepbantuan.

Lihat juga:

Untuk operasi ATAU , lihat:

kenorb
sumber
1

ripgrep

Berikut ini contoh penggunaannya rg:

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

Ini adalah salah satu alat grepping tercepat, karena itu dibangun di atas mesin regex Rust yang menggunakan automata terbatas, SIMD dan optimisasi literal agresif untuk membuat pencarian sangat cepat.

Lihat juga permintaan fitur terkait di GH-875 .

kenorb
sumber
1

Inilah pendapat saya, dan ini berfungsi untuk kata-kata dalam banyak baris:

Gunakan find . -type fdiikuti oleh sebanyak
-exec grep -q 'first_word' {} \;
dan kata kunci terakhir dengan
-exec grep -l 'nth_word' {} \;

-qquiet / silent
-lmenunjukkan file dengan korek api

Berikut ini daftar nama file dengan kata-kata 'kelinci' dan 'lubang' di dalamnya:
find . -type f -exec grep -q 'rabbit' {} \; -exec grep -l 'hole' {} \;

StackRover
sumber
-2

Untuk menemukan SEMUA kata (atau pola), Anda dapat menjalankan grep di loop FOR . Keuntungan utama di sini, adalah mencari dari daftar regex .

Sunting jawaban saya dengan contoh nyata:

# 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

Sekarang mari kita jalankan di file ini:

hhhhhhhhhh

aaaaaaa

bbbbbbbbbb

ababbabaabbaaa

ccccccc

dsfsdf

bbbb

cccdd

A A

caa

# ./search_all_regex_and_error_if_missing.sh

aaaaaaa aa

^ a + $ ditemukan dalam file.

bbbbbbbbb bbbb

^ b + $ ditemukan dalam file.

hhhhhhhhhh

^ h + $ ditemukan dalam file.

Kesalahan: ^ d + $ tidak ditemukan dalam file. Keluar!

Noam Manos
sumber
1
Logika Anda salah - saya meminta ALLoperator, kode Anda berfungsi sebagai ORoperator, bukan AND. Dan btw. untuk itu ( OR) adalah solusi yang lebih mudah diberikan tepat dalam pertanyaan.
greenoldman
@greenoldman Logikanya sederhana: For akan loop pada SEMUA kata / pola dalam daftar, dan jika ditemukan dalam file - akan mencetaknya. Jadi hapus saja yang lain jika Anda tidak perlu bertindak seandainya kata tidak ditemukan.
Noam Manos
1
Saya memahami logika Anda dan juga pertanyaan saya - saya bertanya tentang ANDoperator, artinya file tersebut hanya hit positif jika cocok dengan pola A dan pola B dan pola C dan ... ANDDalam kasus Anda, file adalah hit positif jika cocok pola A atau pola B atau ... Apakah Anda melihat perbedaannya sekarang?
greenoldman
@ greenoldman tidak yakin mengapa Anda berpikir loop ini tidak memeriksa kondisi AND untuk semua pola? Jadi saya sudah mengedit jawaban saya dengan contoh nyata: Ini akan mencari dalam file untuk semua daftar regex, dan yang pertama hilang - akan keluar dengan kesalahan.
Noam Manos
Anda memilikinya tepat di depan mata Anda, Anda memiliki pertandingan positif tepat setelah pertandingan pertama dijalankan. Anda harus memiliki "mengumpulkan" semua hasil dan menghitungnya AND. Maka Anda harus menulis ulang skrip untuk dijalankan pada banyak file - maka mungkin Anda menyadari bahwa pertanyaan sudah dijawab dan upaya Anda tidak membawa apa pun ke meja, maaf.
greenoldman