Apakah mungkin untuk mencocokkan beberapa nomor baris tertentu (bukan rentang) dengan sed?

9

Mempertimbangkan:

echo -e "a\nb\nc\nd\ne\nf\ng\nh" | sed '3,5a test'

Ini akan cocok dengan baris 3, 4 dan 5.

Tetapi saya mencoba untuk mencocokkan hanya baris 3 dan 5 (bukan 4). Dan tambahkan 'tes' setelah mereka.

Aquarius Power
sumber

Jawaban:

9
echo ... | sed -e '3a test' -e '5a test'

Jika operasi lebih kompleks daripada dalam hal ini maka Anda dapat menggunakan struktur seperti ini:

sed 'b pattern; : action; a \
lalala
b end; : pattern; 3b action; 5b action; : end'

Yaitu Anda meletakkan semua perintah yang Anda butuhkan antara b pattern;dan b end;.

Dan Anda menambahkan semua pola Anda (nomor baris atau apa pun) setelahnya : pattern;.

Yang terjadi adalah ini:

  1. Perintah pertama melompati bagian aksi (mungkin lebih mudah untuk membaca jika polanya ada di awal dan b end;langsung sebelum bagian aksi).

  2. Jika suatu pola cocok maka eksekusi melompat ke bagian tindakan. Setelah aksi, bagian eksekusi melompat ke akhir.

Saya mencoba merapikan ini:

sed '3b action; 5b action; b end; : action; a \
lalala
: end'

Dalam satu baris akan seperti:

sed "3b idAction; 5b idAction; b; : idAction; a test"

Portable, Anda perlu menulisnya:

sed '
   3b action
   5b action
   b
   : action
   a\
   lalala'

( btanpa cabang label sampai akhir, jadi Anda tidak perlu endlabel eksplisit , ;adalah karakter yang valid dalam label dalam sedimplementasi standar ).

Hauke ​​Laging
sumber
4
Wow, sediakan subrutin. Cool
glenn jackman
Wow, bisakah Anda mengembangkannya? Mereka terlihat sangat berguna!
terdon
yang dengan 'pola' dan 'tindakan' tampaknya sangat baik karena saya tidak perlu mengulangi 'tes' (atau 'lalala'), harus lebih memahami itu tho thx! :)
Aquarius Power
@terdon Apa yang bisa saya lakukan?
Hauke ​​Laging
Maksud saya yang kedua terlihat hebat tetapi sulit dimengerti dan jika Anda punya waktu, saya akan menghargai penjelasannya.
terdon
5

Dengan sed(lihat jawaban @ HaukeLaging )

Dengan awk:

$ echo -e "a\nb\nc\nd\ne\nf\ng\nh" | awk 'NR==3 || NR==5{$0=$0"\ntest"}1;'

Dengan perl:

$ echo -e "a\nb\nc\nd\ne\nf\ng\nh" | perl -pe '$_ .="test\n" if $. == 3 || $. == 5'
terdon
sumber
Dapat melakukan sedikit perl golf Anda, menggunakan operator "smart match":perl -lnE 'say if $. ~~ @{[3,5]}'
glenn jackman
@glennjackman ah, ya memang, terima kasih. Saya harus mengakui bahwa saya agak waspada ~~, tidak memahaminya sebaik yang seharusnya. Juga, keduanya saydan ~~perlu perl v> = 5.10 kan?
terdon
Saya perlu menambahkan 'tes' setelah kalimat itu, saya mengerti sekarang akan lebih baik untuk menjelaskannya sejak awal. Saya baru saja mengedit OP.
Aquarius Power
@AquariusPower, perl -pE 'say "test" if $. ~~ @{[4,6]}'- ini menambahkan baris: perl's -pmelakukan pencetakan implisit di akhir program yang diberikan.
glenn jackman
@AquariusPower juga melihat jawaban yang diperbarui.
terdon
3

Seperti yang dikatakan @HaukeLaging aleady, perintah ini melakukan apa yang Anda inginkan:

sed -e'3a test' -e'5a test'

Sekarang, ini bisa menjadi agak sulit untuk diketik jika Anda ingin mencocokkan, misalnya, 20 baris.

Dalam kasus ini, dengan asumsi shell Anda mendukung ekspansi brace , Anda dapat menggunakan perintah ini sebagai gantinya:

sed -e{3,5}'a test'

(Perhatikan bahwa kurung kurawal dan koma harus tetap tanda kutip.)

Akibatnya, shell akan meneruskan argumen ke -e3a testdan -e5a testke sed, yang persis seperti yang dilakukan perintah pertama.

Dennis
sumber