Saya memiliki file yang terlihat seperti ini:
<table name="content_analyzer" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer2" primary-key="id">
<type="global" />
</table>
<table name="content_analyzer_items" primary-key="id">
<type="global" />
</table>
Saya perlu mengekstrak apa pun di dalam tanda kutip yang mengikuti name=
, yaitu content_analyzer
, content_analyzer2
dan content_analyzer_items
.
Saya melakukan ini di kotak Linux, jadi solusi menggunakan sed, perl, grep atau bash baik-baik saja.
regex
perl
sed
html-parsing
text-extraction
penengkar
sumber
sumber
Jawaban:
Karena Anda perlu mencocokkan konten tanpa menyertakannya dalam hasil (harus cocok
name="
tetapi bukan bagian dari hasil yang diinginkan), diperlukan beberapa bentuk pencocokan lebar-nol atau pengambilan kelompok. Ini dapat dilakukan dengan mudah dengan alat-alat berikut:Perl
Dengan Perl Anda dapat menggunakan
n
opsi untuk memutar baris demi baris dan mencetak konten dari grup penangkap jika cocok:perl -ne 'print "$1\n" if /name="(.*?)"/' filename
GNU grep
Jika Anda memiliki versi grep yang ditingkatkan, seperti GNU grep, Anda mungkin memiliki
-P
opsi yang tersedia. Opsi ini akan mengaktifkan regex seperti Perl, memungkinkan Anda untuk menggunakan\K
tampilan singkat. Ini akan mengatur ulang posisi pertandingan, jadi apa pun sebelumnya adalah lebar nol.grep -Po 'name="\K.*?(?=")' filename
The
o
merek pilihan grep mencetak hanya teks yang cocok, bukan seluruh baris.Vim - Editor Teks
Cara lain adalah dengan menggunakan editor teks secara langsung. Dengan Vim, salah satu dari berbagai cara untuk menyelesaikan ini adalah dengan menghapus baris tanpa
name=
dan kemudian mengekstrak konten dari baris yang dihasilkan::v/.*name="\v([^"]+).*/d|%s//\1
Grep standar
Jika Anda tidak memiliki akses ke alat ini, karena alasan tertentu, sesuatu yang serupa dapat dicapai dengan grep standar. Namun, tanpa melihat-lihat, ini akan membutuhkan pembersihan nanti:
grep -o 'name="[^"]*"' filename
Catatan tentang menyimpan hasil
Dalam semua perintah di atas, hasil akan dikirim ke
stdout
. Penting untuk diingat bahwa Anda selalu dapat menyimpannya dengan menyalurkannya ke file dengan menambahkan:ke akhir perintah.
sumber
grep
):grep -Po '.*name="\K.*?(?=".*)'
.*
mengesampingkan keduanya , saya harap Anda tidak marah kepada saya. Saya ingin bertanya, apakah Anda melihat manfaat dari pertandingan tidak serakah dibandingkan "apa pun kecuali"
"? Jangan menganggap ini sebagai perkelahian, saya hanya ingin tahu dan saya bukan ahli regex. Juga,\K
tipnya, sangat bagus. Terima kasih Dennis..*
, Anda bisa melakukannyagrep -Po '(?<=name=").*?(?=")'
. The\K
dapat digunakan untuk singkatan, tapi itu benar-benar hanya diperlukan jika pertandingan ke kiri adalah variabel panjang. Dalam kasus seperti ini, alasan menggunakan lookarounds cukup jelas. Operasi ungreedy terlihat sedikit lebih rapi ([^"]*
versus.*?
dan Anda tidak perlu mengulangi karakter jangkar. Saya tidak tahu tentang kecepatan. Itu sangat tergantung pada konteksnya, saya rasa. Saya harap itu membantu.\K
(setelah menelitinya) dan menghapusnya.*
sama: membuatnya terlihat cantik (lebih sederhana). Dan saya tidak pernah berpikir untuk menggunakan.*?
"cara tradisional" yang saya pelajari dari suatu tempat. Tapi tidak serakah di sini sangat masuk akal. Terima kasih Dennis, semoga sukses.Ekspresi regulernya akan menjadi:
.+name="([^"]+)"
Kemudian pengelompokan akan berada di \ 1
sumber
Jika Anda menggunakan Perl, unduh modul untuk mengurai XML: XML :: Simple , XML :: Twig , atau XML :: LibXML . Jangan menemukan kembali roda.
sumber
<type="global"
dalam format yang baik ( misalnya), jadi sebagian besar pengurai XML hanya mengeluh dan mati.Parser HTML harus digunakan untuk tujuan ini daripada ekspresi reguler. Program Perl yang memanfaatkan
HTML::TreeBuilder
:Program
#!/usr/bin/env perl use strict; use warnings; use HTML::TreeBuilder; my $tree = HTML::TreeBuilder->new_from_file( \*DATA ); my @elements = $tree->look_down( sub { defined $_[0]->attr('name') } ); for (@elements) { print $_->attr('name'), "\n"; } __DATA__ <table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table>
Keluaran
sumber
ini bisa melakukannya:
perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'
sumber
Berikut solusi menggunakan HTML tidy & xmlstarlet:
htmlstr=' <table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table> ' echo "$htmlstr" | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null | sed '/type="global"/d' | xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
sumber
Ups, perintah sed harus mendahului perintah rapi tentu saja:
echo "$htmlstr" | sed '/type="global"/d' | tidy -q -c -wrap 0 -numeric -asxml -utf8 --merge-divs yes --merge-spans yes 2>/dev/null | xmlstarlet sel -N x="http://www.w3.org/1999/xhtml" -T -t -m "//x:table" -v '@name' -n
sumber
Jika struktur xml Anda (atau teks pada umumnya) sudah diperbaiki, cara termudah adalah menggunakan
cut
. Untuk kasus spesifik Anda:echo '<table name="content_analyzer" primary-key="id"> <type="global" /> </table> <table name="content_analyzer2" primary-key="id"> <type="global" /> </table> <table name="content_analyzer_items" primary-key="id"> <type="global" /> </table>' | grep name= | cut -f2 -d '"'
sumber