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.