Bagaimana cara mendapatkan beberapa baris dari file dengan regex?
Saya sering ingin mendapatkan beberapa baris / memodifikasi beberapa baris dengan regex. Contoh kasus:
Saya mencoba membaca bagian dari file XML / SGML (mereka belum tentu terbentuk dengan baik atau dalam sintaksis yang dapat diprediksi, sehingga regex akan lebih aman daripada parser yang tepat. Selain itu saya ingin dapat melakukan ini juga sedikit pun sepenuhnya file tidak terstruktur di mana hanya beberapa kata kunci yang dikenal.) dalam skrip shell (berjalan pada Solaris dan Linux).
XML Contoh:
<tag1>
<tag2>bar</tag2>
</tag1>
<tag1>
<tag2>foo</tag2>
</tag1>
Dari sini saya ingin membaca <tag1>
jika berisi foo
suatu tempat di dalamnya.
Regex seperti (<tag1>.*?foo.*?</tag1>)
harus memberikan bagian yang tepat tetapi alat suka grep
dan sed
hanya bekerja untuk saya dengan garis tunggal. Bagaimana saya bisa dapatkan
<tag1>
<tag2>foo</tag2>
</tag1>
dalam contoh ini?
Jawaban:
Jika Anda telah menginstal GNU grep, Anda bisa melakukan pencarian multiline dengan mengirimkan
-P
flag (perl-regex) dan mengaktifkannyaPCRE_DOTALL
dengan(?s)
Jika hal di atas tidak berfungsi pada platform Anda, coba lewati
-z
bendera sebagai tambahan, ini memaksa grep untuk memperlakukan NUL sebagai pemisah baris, menyebabkan seluruh file terlihat seperti satu baris.sumber
(?s)
tipnya(GNU grep) 2.14
pada Debian. Saya menyalin contoh OPs apa adanya (hanya menambahkan baris terakhir) dan menjalankannyagrep
tetapi tidak mendapatkan hasil.grep -ozP
alih-alihgrep -oP
di platform Anda?Jika Anda melakukan hal di atas, mengingat data yang Anda perlihatkan, sebelum garis pembersihan terakhir di sana, Anda harus bekerja dengan
sed
ruang pola yang terlihat seperti:Anda dapat mencetak ruang pola Anda kapan pun Anda suka dengan
l
ook. Anda kemudian dapat mengatasi\n
karakter.Akan menunjukkan kepada Anda setiap baris
sed
memprosesnya pada tahap yangl
disebut.Jadi saya baru saja mengujinya dan perlu satu lagi
\backslash
setelah,comma
di baris pertama, tetapi jika tidak berfungsi sebagaimana mestinya. Di sini saya memasukkannya ke dalam_sed_function
sehingga saya dapat dengan mudah menyebutnya untuk tujuan demonstrasi di seluruh jawaban ini: (berfungsi dengan komentar yang disertakan, tetapi di sini dihapus karena singkatnya)Sekarang kita akan mengganti
p
al
sehingga kita dapat melihat apa yang sedang kita kerjakan saat kita mengembangkan skrip kita dan menghapus demo non-ops?
sehingga baris terakhir dari kitased 3<<\SCRIPT
terlihat seperti:Maka saya akan menjalankannya lagi:
Baik! Jadi saya benar - itu perasaan yang bagus. Sekarang, mari kita mengocok
l
ook kita untuk melihat garis yang menarik tetapi menghapus. Kami akan menghapus arus kamil
dan menambahkannya!{block}
sehingga terlihat seperti:Seperti apa itu sebelum kita memusnahkannya.
Satu hal terakhir yang ingin saya tunjukkan kepada Anda adalah
H
ruang lama saat kami membangunnya. Ada beberapa konsep kunci yang saya harap bisa saya tunjukkan. Jadi saya menghapusl
ook terakhir lagi dan mengubah baris pertama untuk menambahkan mengintip keH
ruang lama di akhir:H
ruang lama bertahan siklus garis - karenanya namanya. Jadi yang sering membuat orang tersandung - ok, yang sering membuat saya tersandung - adalah perlu dihapus setelah Anda menggunakannya. Dalam hal ini saya hanyax
mengubah sekali, jadi ruang penahanan menjadi pola ruang dan sebaliknya dan perubahan ini juga bertahan siklus garis.Efeknya adalah bahwa saya perlu menghapus ruang palka saya yang dulunya ruang pola saya. Saya melakukan ini dengan terlebih dahulu membersihkan ruang pola saat ini dengan:
Yang cukup memilih setiap karakter dan menghapusnya. Saya tidak dapat menggunakan
d
karena ini akan mengakhiri siklus baris saya saat ini dan perintah berikutnya tidak akan selesai, yang akan cukup banyak membuang skrip saya.Ini bekerja dengan cara yang mirip dengan
H
tetapi itu menimpa ruang penyimpanan, jadi saya baru saja menyalin ruang pola kosong saya di atas ruang penyimpanan saya, secara efektif menghapusnya. Sekarang saya bisa:di luar.
Dan itulah cara saya menulis
sed
skrip.sumber
Jawaban @ jamespfinn akan bekerja dengan baik jika file Anda sesederhana contoh Anda. Jika Anda memiliki situasi yang lebih kompleks di mana
<tag1>
dapat menjangkau lebih dari 2 baris, Anda akan memerlukan trik yang sedikit lebih rumit. Sebagai contoh:Script perl akan memproses setiap baris file input Anda dan
if(/<tag1>/){$a=1;}
: variabel$a
diatur ke1
jika tag pembuka (<tag1>
) ditemukan.if($a==1){push @l,$_}
: untuk setiap baris, jika$a
ada1
, tambahkan baris itu ke array@l
.if(/<\/tag1>/)
: jika baris saat ini cocok dengan tag penutup:if(grep {/foo/} @l){print "@l"}
: jika ada garis yang disimpan dalam array@l
(ini adalah garis antara<tag1>
dan</tag1>
) cocok dengan stringfoo
, cetak konten@l
.$a=0; @l=()
: kosongkan daftar (@l=()
) dan atur$a
kembali ke 0.sumber
<tag1>
denganfoo
dan berfungsi dengan baik. Kapan itu gagal untuk Anda?Inilah
sed
alternatifnya:Penjelasan
-n
berarti tidak mencetak garis kecuali diperintahkan./<tag1/
pertama cocok dengan tag pembuka:x
adalah label untuk mengaktifkan lompatan ke titik ini nantiN
menambahkan baris berikutnya ke ruang pola (buffer aktif)./<\/tag1/!b x
berarti jika ruang pola saat ini tidak berisi tag penutup, cabang kex
label yang dibuat sebelumnya. Dengan demikian, kami terus menambahkan garis ke ruang pola hingga kami menemukan tag penutup kami./foo/p
berarti jika ruang pola saat ini cocokfoo
, itu harus dicetak.sumber
Anda bisa melakukannya dengan GNU awk saya pikir, dengan memperlakukan tag akhir sebagai pemisah rekaman misalnya untuk tag akhir yang dikenal
</tag1>
:atau lebih umum (dengan regex untuk tag akhir)
Mengujinya di @ terdon
foo.xml
:sumber
Jika file Anda terstruktur persis seperti yang Anda tunjukkan di atas, Anda dapat menggunakan flag -A (baris setelah) & -B (baris sebelumnya) untuk grep ... misalnya:
Jika versi
grep
dukungan Anda, Anda juga bisa menggunakan opsi yang lebih sederhana-C
(untuk konteks) yang mencetak garis N di sekitarnya:sumber
tail -3 input_file.xml
. Ya itu berfungsi untuk contoh khusus ini, tetapi itu bukan jawaban yang membantu untuk pertanyaan itu.