Bagaimana cara grep untuk teks dalam file dan menampilkan paragraf yang memiliki teks?


24

Di bawah ini adalah teks dalam file:

Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

Saya perlu grep untuk "42B" dan mendapatkan output dari teks di atas seperti:

Pseudo name=Apple
Code=42B
state=fault

Adakah yang punya ide tentang cara mencapai ini menggunakan grep/ awk/ sed?


Anda menandai pertanyaan ini hanya dengan "grep". Apakah Anda hanya mencari solusi "grep"? Dalam pertanyaan Anda tentukan awk & sed juga. Bisakah kita menambahkan tag itu? Saya tidak yakin dengan niat Anda ketika saya mengedit pertanyaan tadi malam.
slm

Jawaban:


38

Dengan awk

awk -v RS='' '/42B/' file

RS=mengubah pemisah rekaman input dari baris baru ke baris kosong. Jika ada bidang dalam catatan yang berisi /42B/cetak catatan.

''(string nol) adalah nilai ajaib yang digunakan untuk mewakili baris kosong menurut POSIX :

Jika RS adalah nol, maka catatan dipisahkan oleh urutan yang terdiri dari <newline>plus satu atau lebih baris kosong, baris kosong awal atau akhir tidak akan menghasilkan catatan kosong di awal atau akhir input, dan a <newline>harus selalu menjadi pemisah lapangan, tidak peduli apa nilai FS .

Paragraf keluaran tidak akan dipisahkan karena pemisah keluaran tetap menjadi satu baris baru. Untuk memastikan bahwa ada garis kosong antara paragraf output, atur pemisah catatan output ke dua baris baru:

awk -v RS='' -v ORS='\n\n' '/42B/' file

1
+1 untuk solusi elegan. Anda tidak perlu mengarahkan ulang file ...
jasonwryan

jari-jarinya berada di autopilot.
llua

2
@jasonwryan, kecuali jika Anda memerlukan akses ke nama file dalam awk ( FILENAME), itu bukan ide yang buruk untuk menggunakan pengalihan karena menghindari masalah untuk nama file yang mengandung =atau dimulai dengan -(atau sedang -), membuat pesan kesalahan yang konsisten, dan menghindari menjalankan awkatau melakukan pengalihan lainnya jika file input tidak dapat dibuka.
Stéphane Chazelas

14

Dengan asumsi data terstruktur sehingga selalu menjadi baris sebelum dan sesudah yang Anda inginkan, Anda dapat menggunakan sakelar grep -A(setelah) dan -B(sebelum) untuk mengatakannya menyertakan 1 baris sebelum pertandingan dan 1 baris setelahnya:

$ grep -A 1 -B 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Jika Anda menginginkan garis angka yang sama sebelum dan sesudah istilah pencarian, Anda dapat menggunakan -Csakelar (konteks):

$ grep -C 1 "42B" sample.txt
Pseudo name=Apple
Code=42B
state=fault

Jika Anda ingin menjadi lebih ketat ketika mencocokkan beberapa baris, Anda dapat menggunakan alat ini pcregrep, untuk mencocokkan pola pada beberapa baris:

$ pcregrep -M 'Pseudo.*\n.*42B.*\nstate.*' sample.txt
Pseudo name=Apple
Code=42B
state=fault

Pola di atas cocok sebagai berikut:

  • -M - beberapa baris
  • 'Pseudo.*\n.*42B.*\nstate.*'- cocok dengan sekelompok string di mana string pertama dimulai dengan kata yang "Pseudo"diikuti oleh karakter apa pun hingga akhir baris \n, diikuti oleh karakter apa saja hingga string "42B"diikuti oleh karakter apa pun hingga akhir baris ( \n), diikuti oleh string "state"diikuti oleh karakter apa saja.

5
-C(konteks) dapat digunakan sebagai jalan pintas, jika -Adan -Bsama.
David Baggerman

@ DavidBaggerman - terima kasih. Menambahkannya ke jawabannya.
slm

Mengapa suara turun? Ini menjawab pertanyaan.
slm

4

Mungkin ada cara yang sama mudahnya untuk melakukannya dengan awk, tetapi dalam perl:

cat file | perl -ne 'BEGIN { $/="\n\n" }; print if $_ =~ /42B/;'

Yang pada dasarnya mengatakan untuk membagi file menjadi potongan-potongan yang dibatasi oleh baris kosong, kemudian hanya mencetak potongan-potongan yang cocok dengan ekspresi reguler Anda.


10
Ini dapat disederhanakan dengan menggunakan opsi dan singkatan, dan kehilangan penggunaan siacat - sia dari ; perl -00 -ne 'print if /42B/' file
tripleee

4

The grepbeberapa rasa Unix memiliki -pbendera untuk "ayat". Saya tahu AIX tidak .

grep -p 42B <myfile>

akan melakukan apa yang Anda minta di sana. YMMV dan GNU grep tidak memiliki flag ini.


Memiliki -p flag akan sangat bagus. Terutama jika digunakan bersama -v sehingga Anda bisa mengecualikan seluruh paragraf dari output.
IllvilJa

2

Solusi perl lainnya, tanpa garis kosong tertinggal:

perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo

Contoh

% perl -00ne 'if ($_ =~ /42B/) {chomp($_); printf "%s\n",$_}' foo
Pseudo name=Apple
Code=42B
state=fault

% cat foo
Pseudo name=Apple
Code=42B
state=fault

Pseudo name=Prance
Code=43B
state=good

1
Atau lebih pendek (dan dengan demikian lebih mudah dibaca), sebagai triplee menulis dalam komentar: perl -00 -ne 'print if /42B/' file.
mivk
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.