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.

Dingin
sumber

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.

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

kekacauan
sumber
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.
Twinkles
sumber
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
DopeGhoti
sumber
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:

kenorb
sumber