Bagaimana saya bisa mencari pola multiline dalam file?

128

Saya perlu menemukan semua file yang berisi pola string tertentu. Solusi pertama yang muncul dalam pikiran adalah menggunakan find piped dengan xargs grep :

find . -iname '*.py' | xargs grep -e 'YOUR_PATTERN'

Tetapi jika saya perlu menemukan pola yang menjangkau lebih dari satu baris, saya terjebak karena vanilla grep tidak dapat menemukan pola multiline.

Oli
sumber
2
Yang ini lebih tua, jadi saya akan mengatakan itu bukan duplikat :)
rogerdpack
@rogerdpack Saat menandai pertanyaan sebagai duplikat, usia pertanyaan adalah masalah tersier, setelah jumlah dan kualitas jawaban serta kualitas pertanyaan.
tripleee

Jawaban:

98

Jadi saya menemukan pcregrep yang merupakan singkatan dari Perl Compatible Regular Expressions GREP .

Misalnya, Anda perlu mencari file di mana variabel ' _name ' langsung diikuti oleh variabel ' _description ':

find . -iname '*.py' | xargs pcregrep -M '_name.*\n.*_description'

Kiat: Anda harus memasukkan karakter pemisah baris dalam pola Anda. Bergantung pada platform Anda, bisa jadi '\ n', \ r ',' \ r \ n ', ...

Oli
sumber
7
Seperti disebutkan oleh halka di bawah ini, "Anda juga dapat membujuk titik wildcard agar cocok dengan baris baru jika Anda menambahkan (?) Pada ekspresi reguler Anda". Kemudian gunakan grep dengan perl regex dengan menambahkan -P. Temukan . -exec grep -nHP '(? s) SELECT. {1,60} FROM. {1,20} table_name' '{}' \;
Jim
8
pcregreptersedia di mac denganbrew install pcre
Jared Beck
1
Bahkan lebih baik: juga menggunakan -Hyang mencetak nama file sebelum setiap pertandingan: pcregrep -HM.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
97

Kenapa kamu tidak pergi untuk awk :

awk '/Start pattern/,/End pattern/' filename
Amit
sumber
2
Ini jauh lebih mudah untuk dipahami dan digunakan awkyang datang dengan kebanyakan sistem * nix.
Ali Karbassi
24
bagus! apakah ada cara untuk membuat pertandingan ini tidak serakah?
marcin
3
Bagaimana Anda hanya mencetak nama file saat ada kecocokan?
bibstha
2
Anda dapat menunjukkan nomor baris pertandingan dengan awk '/Start pattern/,/End pattern/ {printf NR " "; print}' filename. Anda dapat membuatnya lebih cantik dengan memberikan nomor baris lebar tetap: awk '/Start pattern/,/End pattern/ {printf "%-4s ", NR; print}' filename.
Robert
Ini tampaknya bekerja dengan baik pada satu file, bagaimana jika saya ingin mencari di dalam banyak file?
Jinstrong
84

Berikut ini contoh penggunaan GNUgrep :

grep -Pzo '_name.*\n.*_description'

-z/ --null-dataPerlakukan input dan output data sebagai urutan garis.

Lihat juga di sini

ayaz
sumber
1
Itu hanya menyumbang satu karakter baris baru, saya pikir.
Cloud
1
Saya tidak dapat menggunakan grep untuk pencarian multiline, tanpa menggunakan flag -zsehingga tidak membagi pencarian pada satu baris, dan -ountuk mencetak hanya bagian yang cocok.
bbaja42
Saya menemukan bahwa -o menyebabkannya tidak mencetak apa-apa, tetapi -l bekerja untuk mendapatkan daftar file (perintah saya adalah grep -rzl pattern *, -rzo tidak bekerja)
Benubird
5
Saya merekomendasikan '' grep -Pazo '' alih-alih '' -Pzo '' untuk file non-ASCII. Lebih baik karena saklar -z pada file non-ASCII dapat memicu perilaku "data biner" grep yang mengubah nilai kembali. Ganti '' -a | --text '' mencegahnya.
rloth
Tidak bekerja pada Mac dengan git diinstal olehbrew reinstall --with-pcre git
Quanlong
21

grep -Pjuga menggunakan libpcre, tetapi jauh lebih banyak diinstal. Untuk menemukan titlebagian lengkap dari dokumen html, bahkan jika itu mencakup beberapa baris, Anda dapat menggunakan ini:

grep -P '(?s)<title>.*</title>' example.html

Karena proyek PCRE menerapkan standar perl, gunakan dokumentasi perl untuk referensi:

bukzor
sumber
Hmm mencoba ini sekarang dan sepertinya tidak berhasil ... gist.github.com/rdp/0286d91624930bd11d0169d6a6337c33
rogerdpack
Saya tidak tahu grep memiliki opsi ini. Mungkin karena ini: Ini sangat eksperimental dan grep -P dapat memperingatkan fitur yang tidak diimplementasikan. ; itu di bawah CentOS 7. Di bawah Fedora 29: Ini eksperimental dan grep -P dapat memperingatkan fitur yang tidak diimplementasikan . Tentu saja di BSD grep itu tidak ada sama sekali. Akan menyenangkan jika itu tidak begitu eksperimental tetapi itu baik untuk diingatkan tentang itu - sedikit meskipun saya akan menggunakannya.
Pryftan
17

Ini adalah contoh yang lebih berguna:

pcregrep -Mi "<title>(.*\n){0,5}</title>" afile.html

Itu mencari tag judul dalam file html bahkan jika itu membentang hingga 5 baris.

Ini adalah contoh garis tak terbatas:

pcregrep -Mi "(?s)<title>.*</title>" example.html 
Oli
sumber
4
Terima kasih untuk ini. Saya terjebak tidak menyadari bahwa wildcard tidak akan cocok dengan karakter baris baru.
matt
7
@matt: Anda juga dapat membujuk titik wildcard agar sesuai dengan baris baru jika Anda menambahkan (?s)ekspresi reguler Anda, seperti:"(?s)<html>.*</html>"
lubomir.brindza
@ tikar Tentu saja Anda dapat memeriksa $(pada akhir pola) untuk menandakan itu adalah akhir dari garis - meskipun itu tidak sama dengan membantu Anda menemukan beberapa pola garis. Lihat juga glob(7). Anda mungkin juga menemukan situs web ini menarik: regular-expressions.info
Pryftan
8

Dengan pencari perak :

ag 'abc.*(\n|.)*efg'

Optimalisasi kecepatan pencari perak mungkin dapat bersinar di sini.

Shwaydogg
sumber
4

Anda dapat menggunakan alternatif grep sift sini (disclaimer: Saya penulis).

Ini mendukung pencocokan multiline dan membatasi pencarian untuk jenis file tertentu di luar kotak:

sift -m --files '* .py' 'YOUR_PATTERN'

(Cari semua file * .py untuk pola regil multiline yang ditentukan)

Ini tersedia untuk semua sistem operasi utama. Lihatlah halaman sampel untuk melihat bagaimana itu dapat digunakan untuk mengekstraksi nilai multiline dari file XML.

svent
sumber
3

Jawaban ini mungkin bermanfaat:

Regex (grep) untuk pencarian multi-line diperlukan

Untuk menemukan secara rekursif Anda dapat menggunakan flag -R (rekursif) dan --include (pola GLOB). Lihat:

Gunakan grep --exclude / - include sintaks untuk tidak grep melalui file-file tertentu

Albfan
sumber
@ Ɖiamond ǤeezeƦ perhatikan bahwa mengedit posting di LQP ( stackoverflow.com/review/low-quality-posts/19341146 ) membatalkan ulasan, jadi edit saja jika Anda yakin posting tersebut perlu dipertahankan.
fedorqui 'SO berhenti merugikan'
2

@Marcin: contoh awk tidak serakah:

awk '{if ($0 ~ /Start pattern/) {triggered=1;}if (triggered) {print; if ($0 ~ /End pattern/) { exit;}}}' filename
Martin
sumber
2
perl -ne 'print if (/begin pattern/../end pattern/)' filename
pbal
sumber
Ini mencetak seluruh file
Herbert
1

Menggunakan ex/ vieditor dan opsi globstar (sintaksis mirip dengan awkdan sed):

ex +"/string1/,/string3/p" -R -scq! file.txt

di mana aaatitik awal Anda, dan bbbteks akhir Anda.

Untuk mencari secara rekursif, coba:

ex +"/aaa/,/bbb/p" -scq! **/*.py

Catatan: Untuk mengaktifkan **sintaks, jalankan shopt -s globstar(Bash 4 atau zsh).

kenorb
sumber