Bagaimana cara membaca baris tertentu setelah menemukan beberapa teks?


12

Bagaimana saya bisa membaca sejumlah baris setelah menemukan beberapa teks?

Misalnya.:

Baca 2 baris berikutnya setelah menemukan "Unix" pada:

Test 1
Test 2
Test 3
Test 4
UNIX
Test 5
Test 6
Test 7
Test 8
Test 9

Hasilnya bisa:

Test 5
Test 6

Catatan: "Unix" pada contoh terakhir adalah argumen, dan karenanya, bisa berupa teks lain.

Apa yang saya punya:

Saya masih kehabisan ide, hanya perlu cahaya. Berpikir membuat skrip lain untuk melakukan itu.

Jawaban:


10

Sebuah awksolusi:

$ awk '$0 == "UNIX" {i=1;next};i && i++ <= 2' file
Test 5
Test 6

Penjelasan

  • /^UNIX$/{i=1;next}: jika kita melihat UNIX, kita mengatur variabel i = 1, memproses ke input selanjutnya.

  • Jika variabel idiatur (artinya kita melihat UNIX), i && i++ <= 2hanya dievaluasi ke nilai sebenarnya dalam dua baris berikutnya setelah UNIX, menyebabkan awktindakan default yang dilakukan print $0.

  • Sebelum melihat UNIX, itidak didefinisikan dan dimulai pada baris ke-3 setelahnya UNIX, imemiliki nilai lebih besar dari 2, yang membuat ekspresi i && i++ <= 2dievaluasi menjadi false, sehingga awktidak melakukan apa-apa.


Setelah menguji solusi Anda, saya mendapatkan pesan kesalahan ini: error systax near line 1 bailing out dekat line 1
— Cold

@ Dingin: Apa yang Anda jalankan? Harap perhatikan bahwa $tanda di awal jawaban saya adalah shell prompt, bukan bagian dari awkperintah.
— cuonglm

Varian lain:awk '/^UNIX$/ {s=NR;next} s && NR<=s+2'
— musiphil

Saya tahu bahwa @cuonglm
— Dingin

@ Dingin: Apa OS Anda?
— cuonglm

12

Sebuah grepsolusi:

grep -A2 -P '^UNIX$' file

Penjelasan: -A berarti: mencetak dua baris berikutnya setelah pertandingan

Atau awk:

awk '$0=="UNIX"{getline; print; getline; print}' file

Penjelasan: Pernyataan itu mencari UNIX di baris ( $0=="UNIX"). Jika itu diberikan, ia akan mendapatkan buffer berikutnya ( getline) dan mencetak buffer ( print). Ini dilakukan dua kali.

Atau gunakan sed:

sed -n '/^UNIX$/{n;p;n;p}' file

Penjelasan: Itu cocok untuk UNIX ( /^UNIX$/). Jika ini ditemukan, ia menjalankan bagian dalam {...}. nberarti selanjutnya, pberarti cetak. Ini dilakukan dua kali juga.


Terima kasih @chaos, saya akan mencoba 2 opsi terakhir yang Anda berikan. Tolong tambahkan beberapa penjelasan dari setiap opsi, saya akan compreend dan lakukan.
— Dingin

Jika jumlah baris berubah, berapa banyak perubahan yang akan saya buat pada dua opsi terakhir? Terima kasih
— Dingin

@Dingin lihat edit saya. Untuk mengubah nomor jika baris mengulangi getline; print;bagian dalam awkpernyataan atau n;p;bagian dalam sedpernyataan.
— chaos

Terima kasih @chaos, tetapi semakin tinggi jumlah baris meningkat ekspresi dan perubahannya tidak layak menurut saya. Tidakkah kamu berpikir? Jika 100 baris?
— Dingin

@ Dingin Lalu saya akan menggunakan solusi grep dengan grep -A100 -P '^UNIX$' file | tail -n +2. Bagian ekornya untuk menghilangkan hak gadai pertama. Dalam yang lain (sed, awk) Anda harus menulis loop, apa yang membuatnya lebih sederhana.
— chaos

4
grep -A 2 UNIX file.txt

Halaman manual grep menjelaskan opsi sebagai berikut:

  -A NUM, --after-context=NUM
      Print NUM  lines  of  trailing  context  after  matching  lines.
      Places  a  line  containing  --  between  contiguous  groups  of
      matches.

Hai @ Berkedip, jawaban yang bagus, tetapi grep saya hanya memiliki opsi ini "hblcnsviw". Tapi logikanya bagus. terima kasih
— Dingin

Ini akan mencetak UNIXdalam output juga.
— cuonglm

Untuk menghilangkan UNIX, pipa ke tail: [...] | tail -n +1, atau untuk sed: [...] | sed '1d'.
— DopeGhoti

1
@DopeGhoti: saran Anda taildan sed '1d'hanya berfungsi dengan benar jika UNIXhanya muncul sekali dalam teks input. Semua jawaban lain memungkinkan untuk beberapa kejadian. Mungkin lebih baik menyarankan ... | grep -v UNIX. Harus diakui, ini menjadi berantakan jika UNIXmuncul pada baris 15 dan 17.
— G-Man Mengatakan 'Reinstate Monica'

Poin bagus. Saya cukup yakin itu bisa dilakukan seddengan beberapa bentuk sed '/UNIX/d;n;n;p/' /path/to/file, yang saya hanya sussed dan kirimkan sebagai jawaban.
— DopeGhoti

0

Ini tampaknya berhasil dengan baik:

sed -n '/UNIX/{n;p;n;p}' /path/to/file

Bukti dari konsep:

$ for i in {1..9}; do echo $i; done | sed -n '/4/{n;p;n;p}'
5
6

1
Subshell di sekitar forloop Anda tidak perlu.
— Dijeda sampai pemberitahuan lebih lanjut.

Memang tidak; itu adalah sisa dari beberapa faffery lain aku berada di tengah-tengah cangkang itu pada hari sebelumnya. Parens dihapus.
— DopeGhoti

0

Anda bisa menggunakan ex:

ex -s +'1,/UNIX/d|%p|q!' file_or_/dev/stdin

dimana:

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.