Apakah sed mengabaikan baris yang tidak cocok

94

Bagaimana cara membuat sedgaris pencocokan filter menurut beberapa ekspresi, tetapi mengabaikan garis yang tidak cocok, alih-alih membiarkannya dicetak?

Sebagai contoh nyata, saya ingin menjalankan scalac(kompiler Scala) pada satu set file, dan membaca dari -verboseoutputnya .classfile yang dibuat. scalac -verbosemengeluarkan banyak pesan, tetapi kami hanya tertarik pada yang berbentuk [wrote some-class-name.class]. Apa yang saat ini saya lakukan adalah ini ( |&adalah cara bash 4.0 untuk menyalurkan stderr ke program berikutnya):

$ scalac -verbose some-file.scala ... |& sed 's/^\[wrote \(.*\.class\)\]$/\1/'

Ini akan mengekstrak nama file dari pesan yang kami minati, tetapi juga akan membiarkan semua pesan lain lewat tanpa diubah! Tentu saja kita bisa melakukan ini:

$ scalac -verbose some-file.scala ... |& grep '^\[wrote .*\.class\]$' |
  sed 's/^\[wrote \(.*\.class\)\]$/\1/'

yang berfungsi tetapi terlihat sangat mirip dengan masalah sebenarnya, yaitu bagaimana menginstruksikan seduntuk mengabaikan baris yang tidak cocok dari input. Jadi bagaimana kita melakukannya?

Paggas
sumber
2
Jawaban yang diterima haruslah oleh mouviciel: stackoverflow.com/a/1665574/869951
Oliver

Jawaban:

90

Cara lain dengan sed biasa:

sed -e 's/.../.../;t;d'

s///adalah substituion, ttanpa label apa pun secara kondisional melewatkan semua perintah berikut, dmenghapus baris.

Tidak perlu perl atau grep.

(diedit setelah saran Nicholas Riley)

liori
sumber
3
Pada OS X 10.8.2 saya harus memisahkan txdan ddengan baris baru daripada titik koma seperti yang saya dapatkan undefined label 'x;d;:x'.
davidchambers
6
Bahkan lebih baik: sed -e 's/.../.../' -e 'tx' -e 'd' -e ':x'(disarankan dalam komentar pada pertanyaan serupa ).
davidchambers
1
't' akan mentransfer ke akhir naskah jika tidak ada label yang disediakan, sehingga sederhana: sed -e 's/.../.../' -e 't' -e 'd'.
Nicholas Riley
Orang-orang tidak tahu arti dari -eopsi jadi jangan menyebutkannya secara umum.
Fredrick Gauss
246

Jika Anda tidak ingin mencetak garis yang tidak cocok, Anda dapat menggunakan kombinasi

  • -n opsi yang memberi tahu sed untuk tidak mencetak
  • p bendera yang memberitahu sed untuk mencetak apa yang cocok

Ini memberi:

sed -n 's/.../.../p'
mouviciel
sumber
3
Satu kelemahan dari pendekatan ini adalah jika Anda memiliki beberapa ekspresi yang cocok, hasilnya juga akan dicetak beberapa kali. Misalnya: echo foo | sed -n -e 's/foo/bar/p' -e 's/bar/oof/p'akan menampilkan keduanya bardan oofpada baris terpisah. Meskipun variasi goto-label tidak dapat menangani banyak pola karena akan menghapus garis jika pola pertama tidak cocok.
Rapsey
@Rapsey itu karena Anda menyuruhnya untuk mencetak dua kali. Dalam perintah sed tunggal Anda telah menyuruhnya untuk mencetak dua kali, setiap contoh dicetak in-situ (atau mungkin buffer). Anda harus menyalurkan alih-alih -e atau hanya meletakkan 'p' di -e terakhir.
mikroba
@Rapsey: lihat jawaban saya tentang hal yang relevan ini
Amessihel
@microbial, ini tidak akan berfungsi karena p flag terakhir akan bekerja pada setiap baris yang cocok dengan ekspresi substitusi terakhir.
Amessihel
1

Gunakan Perl:

... |& perl -ne 'print "$1\n" if /^\[wrote (.*\.class)\]$/'
Greg Hewgill
sumber
0

Rapsey mengangkat poin yang relevan tentang beberapa ekspresi substitusi.

  • Pertama, mengutip jawaban Unix SE , Anda dapat "memberi awalan pada sebagian besar perintah sed dengan alamat untuk membatasi baris yang diterapkan".
  • Kedua, Anda dapat mengelompokkan perintah dalam tanda kurung kurawal {}(dipisahkan dengan titik koma ;atau baris baru)
  • Ketiga, tambahkan bendera cetak p pada substitusi terakhir

Sintaksis:

sed -n -e '/^given_regexp/ {s/regexp1/replacement1/flags1;[...];s/regexp1/replacement1/flagsnp}'

Contoh (lihat dokumen di sini untuk lebih jelasnya):

  • Kode:

    sed -n -e '/^ha/ {s/h/k/gp;s/a/e/g}' <<SAMPLE
    haha
    hihi
    SAMPLE
    
  • Hasil:

    kaka
    
Amessihel
sumber
-1
sed -n '/.../!p'

Tidak perlu substitusi.

Jelas sekali
sumber