Cara memilih garis di antara dua pola penanda yang mungkin muncul beberapa kali dengan awk / sed


119

Menggunakan awkatau sedbagaimana saya dapat memilih garis yang muncul di antara dua pola penanda yang berbeda? Mungkin ada beberapa bagian yang ditandai dengan pola ini.

Contoh: Misalkan file tersebut berisi:

abc
def1
ghi1
jkl1
mno
abc
def2
ghi2
jkl2
mno
pqr
stu

Dan pola awal abcdan pola akhir adalah mno Jadi, saya membutuhkan keluaran sebagai:

def1
ghi1
jkl1
def2
ghi2
jkl2

Saya menggunakan sed untuk mencocokkan pola sekali:

sed -e '1,/abc/d' -e '/mno/,$d' <FILE>

Apakah ada cara dalam sedatau awk melakukannya berulang-ulang sampai akhir file?

Jawaban:


188

Gunakan awkdengan bendera untuk memicu pencetakan bila perlu:

$ awk '/abc/{flag=1;next}/mno/{flag=0}flag' file
def1
ghi1
jkl1
def2
ghi2
jkl2

Bagaimana cara kerjanya?

  • /abc/cocok juga dengan baris yang memiliki teks /mno/ini.
  • /abc/{flag=1;next}mengatur flagkapan teks abcditemukan. Kemudian, itu melewati garis.
  • /mno/{flag=0}tidak menyetel flagsaat teks mnoditemukan.
  • Yang terakhir flagadalah pola dengan tindakan default, yaitu print $0: jika flagsama dengan 1 baris akan dicetak.

Untuk penjelasan dan contoh yang lebih rinci, bersama dengan kasus-kasus ketika pola ditampilkan atau tidak, lihat Bagaimana memilih garis di antara dua pola? .


30
Jika Anda ingin mencetak semuanya di antara dan termasuk pola, Anda dapat menggunakan awk '/abc/{a=1}/mno/{print;a=0}a' file.
scai

6
Ya, @scai! atau bahkan awk '/abc/{a=1} a; /mno/{a=0}' file- dengan ini, menempatkan akondisi sebelum /mno/kita membuatnya mengevaluasi baris sebagai benar (dan mencetaknya) sebelum pengaturan a=0. Dengan cara ini kita bisa menghindari menulis print.
fedorqui 'JADI berhenti melukai'

12
@scai @fedorqui Untuk menyertakan keluaran pola, Anda dapat melakukannyaawk '/abc/,/mno/' file
Jotne

1
@hkasera awk '/abc/{flag=1}/mno/{flag=0}flag' fileharus membuat.
fedorqui 'JADI berhenti merusak'

2
@EirNym itu adalah skenario aneh yang dapat ditangani dengan cara yang sangat berbeda: baris mana yang ingin Anda cetak? Mungkin awk 'flag; /PAT1/{flag=1; next} /PAT1/{flag=0}' fileakan membuat.
fedorqui 'JADI berhenti melukai'

45

Menggunakan sed:

sed -n -e '/^abc$/,/^mno$/{ /^abc$/d; /^mno$/d; p; }'

The -nberarti pilihan tidak mencetak secara default.

Pola mencari baris yang berisi just abcto just mno, lalu menjalankan tindakan di { ... }. Tindakan pertama menghapus abcbaris; baris kedua mno; dan pmencetak garis yang tersisa. Anda dapat melonggarkan ekspresi reguler sesuai kebutuhan. Setiap baris di luar kisaran abc.. mnotidak dicetak.


Terima kasih atas balasannya dan untuk penjelasannya! :)
dvai

@JonathanLeffler dapatkah saya tahu apa tujuan penggunaan-e
Kasun Siyambalapitiya

1
@KasunSiyambalapitiya: Biasanya artinya saya suka menggunakannya. Secara formal, ini menentukan bahwa argumen berikutnya adalah (bagian dari) skrip yang sedharus dieksekusi. Jika Anda ingin atau perlu menggunakan beberapa argumen untuk memasukkan seluruh skrip, maka Anda harus menggunakan -esebelum setiap argumen tersebut; jika tidak, itu opsional (tapi eksplisit).
Jonathan Leffler

@JonathanLeffler Thanks
Kasun Siyambalapitiya

Bagus! (Saya lebih suka sed daripada awk.) Saat menggunakan ekspresi reguler yang kompleks, alangkah baiknya jika tidak mengulanginya. Apakah tidak mungkin menghapus baris pertama / terakhir dari rentang "yang dipilih"? Atau untuk pertama kali menerapkan dke semua baris hingga pertandingan pertama, dan kemudian dke semua baris yang dimulai dengan pertandingan kedua?
hans_meine

18

Ini mungkin berhasil untuk Anda (GNU sed):

sed '/^abc$/,/^mno$/{//!b};d' file

Hapus semua baris kecuali yang antara baris dimulai abcdanmno



Ini luar biasa. The {//!b}mencegah yang abcdan mnodari yang termasuk dalam output, tapi aku tidak tahu bagaimana. Bisakah Anda menjelaskan?
Brendan

1
@ Brendan instruksi //!bmembaca jika baris saat ini bukan salah satu baris yang cocok dengan rentang, putus dan karena itu mencetak baris tersebut jika tidak semua baris lainnya akan dihapus.
potong

13
sed '/^abc$/,/^mno$/!d;//d' file

golf dua karakter lebih baik daripada ppotong {//!b};d

Garis miring kosong //berarti: "gunakan kembali ekspresi reguler terakhir yang digunakan". dan perintahnya melakukan hal yang sama seperti yang lebih bisa dimengerti:

sed '/^abc$/,/^mno$/!d;/^abc$/d;/^mno$/d' file

Ini sepertinya POSIX :

Jika RE kosong (yaitu, tidak ada pola yang ditentukan) sed akan berperilaku seolah-olah RE terakhir yang digunakan dalam perintah terakhir yang diterapkan (baik sebagai alamat atau sebagai bagian dari perintah pengganti) telah ditentukan.


1
Saya pikir solusi kedua tidak akan menghasilkan apa-apa karena perintah kedua juga merupakan rentang. Namun pujian untuk yang pertama.
potong

@potong benar! Saya harus belajar lebih banyak mengapa yang pertama berhasil. Terima kasih!
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

7

Dari tautan tanggapan sebelumnya, yang melakukannya untuk saya, berjalan kshdi Solaris, adalah ini:

sed '1,/firstmatch/d;/secondmatch/,$d'
  • 1,/firstmatch/d: dari baris 1 hingga pertama kali Anda temukan firstmatch, hapus.
  • /secondmatch/,$d: dari kemunculan pertama secondmatchhingga akhir file, hapus.
  • Titik koma memisahkan dua perintah yang dijalankan secara berurutan.

Penasaran saja, mengapa range limiter ( 1,) muncul sebelumnya /firstmatch/? Saya menduga ini juga bisa diutarakan '/firstmatch/1,d;/secondmatch,$d'?
Luke Davis

2
Dengan "1, / firstmatch / d" Anda mengatakan "dari baris 1 sampai pertama kali Anda menemukan 'firstmatch', delete". Sedangkan dengan "/ secondmatch /, $ d" you say "dari kemunculan pertama 'secondmatch' sampai akhir file, delete". titik koma memisahkan dua perintah, yang dijalankan secara berurutan.
FanDeLaU

2
perl -lne 'print if((/abc/../mno/) && !(/abc/||/mno/))' your_file

Baik untuk mengetahui padanan perl karena ini adalah alternatif yang cukup bagus untuk awk dan sed.
akhan

2

sesuatu seperti ini berhasil untuk saya:

file.awk:

BEGIN {
    record=0
}

/^abc$/ {
    record=1
}

/^mno$/ {
    record=0;
    print "s="s;
    s=""
}

!/^abc|mno$/ {
    if (record==1) {
        s = s"\n"$0
    }   
}

menggunakan: awk -f file.awk data...

edit: Solusi O_o fedorqui jauh lebih baik / lebih cantik dari saya.


3
Dalam GNU awk if (record=1)harus if (record==1), yaitu ganda = - lihat operator perbandingan melongo
George Hawkins

2

Jawaban Don_crissti dari Tampilkan hanya teks antara 2 pola yang cocok ?

firstmatch="abc"
secondmatch="cdf"
sed "/$firstmatch/,/$secondmatch/!d;//d" infile

yang jauh lebih efisien daripada aplikasi AWK, lihat di sini .


Saya tidak berpikir menghubungkan perbandingan waktu masuk akal di sini, karena persyaratan pertanyaannya sangat berbeda, maka solusinya.
fedorqui 'JADI berhenti merugikan'

2
Saya tidak setuju karena kita harus memiliki beberapa kriteria untuk membandingkan jawaban. Hanya sedikit yang memiliki aplikasi SED.
Léo Léopold Hertz 준영

0

Saya mencoba menggunakan awkuntuk mencetak garis antara dua pola sementara pola2 juga cocok dengan pola1 . Dan garis pattern1 juga harus dicetak.

misalnya sumber

package AAA
aaa
bbb
ccc
package BBB
ddd
eee
package CCC
fff
ggg
hhh
iii
package DDD
jjj

harus memiliki keluaran

package BBB
ddd
eee

Di mana pola1 adalah package BBB, pola2 adalah package \w*. Perhatikan bahwa CCCitu bukan nilai yang diketahui sehingga tidak dapat dicocokkan secara harfiah.

Dalam hal ini, baik @scai awk '/abc/{a=1}/mno/{print;a=0}a' filemaupun @fedorqui tidak cocok awk '/abc/{a=1} a; /mno/{a=0}' fileuntuk saya.

Akhirnya saya berhasil mengatasinya dengan awk '/package BBB/{flag=1;print;next}/package \w*/{flag=0}flag' file, haha

Sedikit lebih banyak usaha menghasilkan awk '/package BBB/{flag=1;print;next}flag;/package \w*/{flag=0}' file, untuk mencetak garis pola2 juga, yaitu,

package BBB
ddd
eee
package CCC
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.