sed atau awk: hapus n baris mengikuti pola

105

Bagaimana cara mencampur pola dan rentang numerik dalam sed (atau alat serupa - awk misalnya)? Yang ingin saya lakukan adalah mencocokkan baris tertentu dalam file, dan menghapus baris n berikutnya sebelum melanjutkan, dan saya ingin melakukannya sebagai bagian dari pipeline.

Martin DeMello
sumber

Jawaban:

187

Saya akan mencoba ini.

Untuk menghapus 5 baris setelah pola (termasuk baris dengan pola):

sed -e '/pattern/,+5d' file.txt

Untuk menghapus 5 baris setelah pola (tidak termasuk baris dengan pola):

sed -e '/pattern/{n;N;N;N;N;d}' file.txt
dogbane
sumber
14
Perhatikan bahwa +Npolanya adalah ekstensi GNU. Ubah yang pertama nmenjadi an Ndalam contoh kedua Anda untuk membuatnya menyertakan garis dengan pola.
Dijeda sampai pemberitahuan lebih lanjut.
2
bagaimana cara menghapus semua baris setelah polanya cocok? Saya menggunakan sed -e '/ <! - # content end -> </div> /, $ d' out.txt tetapi memberikan kesalahan yang mengatakan: sed: -e ekspresi # 1, char 24: karakter tambahan setelah perintah Terima kasih sebelumnya.
N mol
8
Apa yang terjadi serupa tetapi sedikit berbeda dalam setiap kasus. Dalam resep pertama, /pattern/,+5tentukan rentang, yang dimulai dengan baris yang berisi "pola" ( /pattern/) dan berakhir 5 baris kemudian ( +5). Karakter terakhir dadalah perintah untuk dijalankan pada setiap baris dalam rentang tersebut, yaitu "hapus". Dalam resep kedua, alih-alih mencocokkan rentang, itu cocok hanya pada baris yang berisi pola ( /pattern/) dan kemudian menjalankan serangkaian perintah:, {n;N;N;N;N;d}yang pada dasarnya mencetak baris berikutnya ( n) dan kemudian membaca dan akhirnya membuang 4 baris berikutnya ( N;N;N;N;d).
pimlottc
18
Pada sistem Mac / OS X, Anda perlu menambahkan titik koma sebelum tanda kurung tutup:sed -e '/pattern/{n;N;N;N;N;d;}' file.txt
AvL
1
Untuk kelengkapan: Untuk menghapus semua baris mengikuti pola tertentu something lakukan :, di sed -E '/^something$/,$d'mana -Eportabilitas diperpanjang regex POSIX.
not2qubit
7

Tanpa ekstensi GNU (misalnya di macOS):

Untuk menghapus 5 baris setelah pola (termasuk baris dengan pola)

 sed -e '/pattern/{N;N;N;N;d;}'

Tambahkan -i ''untuk mengedit di tempat.

thakis
sumber
6

awkSolusi sederhana :

Asumsikan bahwa ekspresi reguler yang digunakan untuk menemukan garis yang cocok disimpan dalam variabel shell $regex, dan jumlah baris yang dilewati $count.

Jika garis yang cocok juga harus dilewati ( $count + 1garis dilewati):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; next } --skip >= 0 { next } 1'

Jika baris yang cocok tidak boleh dilewati ( $countbaris setelah pertandingan dilewati):

... | awk -v regex="$regex" -v count="$count" \
  '$0 ~ regex { skip=count; print; next } --skip >= 0 { next } 1'

Penjelasan:

  • -v regex="$regex" -v count="$count"mendefinisikan awkvariabel berdasarkan variabel shell dengan nama yang sama.
  • $0 ~ regex cocok dengan garis minat
    • { skip=count; next }menginisialisasi penghitungan lompatan dan melanjutkan ke baris berikutnya, secara efektif melompati baris yang cocok; dalam solusi kedua, printbefore nextmemastikan bahwa itu tidak dilewati.
    • --skip >= 0 mengurangi jumlah lompatan dan mengambil tindakan jika (masih)> = 0, menyiratkan bahwa garis yang ada harus dilewati.
    • { next } melanjutkan ke baris berikutnya, secara efektif melewati baris saat ini
  • 1adalah singkatan yang umum digunakan untuk { print }; artinya, baris saat ini hanya dicetak
    • Hanya baris yang tidak cocok dan tidak dilewati yang mencapai perintah ini.
    • Alasan yang 1setara dengan { print }adalah yang 1diartikan sebagai pola Boolean yang menurut definisi selalu bernilai true, yang berarti bahwa tindakan (blok) yang terkait dieksekusi tanpa syarat. Karena tidak ada tindakan terkait dalam kasus ini, awkdefaultnya adalah mencetak garis.
mklement0
sumber
3

Ini mungkin berhasil untuk Anda:

cat <<! >pattern_number.txt
> 5 3
> 10 1
> 15 5
> !
sed 's|\(\S*\) \(\S*\)|/\1/,+\2{//!d}|' pattern_number.txt |
sed -f - <(seq 21)
1 
2
3
4
5
9
10
12
13
14
15
21
potong
sumber
10
Wow, itu samar.
pimlottc
3
Solusi yang cerdas (meskipun spesifik GNU-Sed), tetapi hanya sedikit orang yang akan mendapat manfaat darinya, kecuali Anda menambahkan penjelasan. pattern_number.txtadalah file 2 kolom yang berisi pola yang akan dicocokkan di kolom pertama, dan di kolom ke-2 jumlah baris yang harus dilewati. sedPerintah pertama mengubah file menjadi sedskrip yang melakukan pencocokan dan skipping yang sesuai; skrip itu disediakan melalui -fdan stdin ( -) ke sedperintah ke-2 . sedPerintah ke-2 beroperasi pada contoh file masukan ad-hoc yang dibentuk dari keluaran seq 21untuk menunjukkan bahwa perintah tersebut berfungsi.
mklement0
Selain itu, solusinya dilengkapi dengan satu peringatan: metode yang digunakan untuk tidak melewati baris pertama (yang cocok dengan pola) memiliki efek samping juga tidak melewatkan baris duplikat dalam rentang.
mklement0
Itu adalah penggunaan sed yang mengesankan.
Travis Rodman
3

Menggunakan Perl

$ cat delete_5lines.txt
1
2
3
4
5 hello
6
7
8
9
10
11 hai
$ perl -ne ' BEGIN{$y=1} $y=$.  if /hello/ ; print if $y==1 or $.-$y > 5 ' delete_5lines.txt
1
2
3
4
11 hai
$
stack0114106
sumber
2

Solusi ini memungkinkan Anda untuk meneruskan "n" sebagai parameter dan itu akan membaca pola Anda dari sebuah file:

awk -v n=5 '
    NR == FNR {pattern[$0]; next}
    {
        for (patt in pattern) {
            if ($0 ~ patt) {
                print # remove if you want to exclude a matched line
                for (i=0; i<n; i++) getline
                next
            }
        }
        print
    }
' file.with.patterns -

File bernama "-" berarti stdin untuk awk, jadi ini cocok untuk pipeline Anda

glenn jackman
sumber
2
awk mampu menjadi lebih seperti perl dari yang saya sadari!
Martin DeMello