Grep, regex veya perl ile bir kalıbı takip eden dizge nasıl çıkarılır


91

Şuna benzeyen bir dosyam var:

    <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>

Aşağıdaki alıntılardan herhangi bir şey çıkarmam gerekiyor name=, yani content_analyzer, content_analyzer2ve content_analyzer_items.

Bunu bir Linux kutusunda yapıyorum, bu nedenle sed, perl, grep veya bash kullanan bir çözüm iyi.


5
utanmana gerek yok, buraya hoşgeldin!
Benoit


Faydalı yorumlar için herkese teşekkürler. XML'nin düzgün biçimlendirilmemesinden dolayı özür dilerim. Basitleştirmek için bazı etiketleri sildim.
wrangler

Yanıtlar:


172

İçeriği sonuca dahil etmeden eşleştirmeniz gerektiğinden (eşleşmelidir, name=" ancak bu istenen sonucun parçası değildir) bir tür sıfır genişlik eşleştirme veya grup yakalama gereklidir. Bu, aşağıdaki araçlarla kolayca yapılabilir:

Perl

Perl ile n satır satır döngü seçeneğini kullanabilir ve bir yakalama grubunun içeriğini eşleşirse yazdırabilirsiniz:

perl -ne 'print "$1\n" if /name="(.*?)"/' filename

GNU grep

GNU grep gibi gelişmiş bir grep sürümüne sahipseniz, bu -Pseçeneğe sahip olabilirsiniz . Bu seçenek Perl benzeri regex'i etkinleştirerek, \Khangisinin arkaya bakma kısayolunu kullanmanıza izin verir . Eşleşme konumunu sıfırlayacaktır, yani önündeki her şey sıfır genişliktedir.

grep -Po 'name="\K.*?(?=")' filename

o Opsiyon markaları yerine bütün hattının sadece eşleşen metin yazdırmak grep.

Vim - Metin Düzenleyici

Başka bir yol da doğrudan bir metin düzenleyici kullanmaktır. Vim ile bunu gerçekleştirmenin çeşitli yollarından biri, satırları olmadan silmek name=ve sonra ortaya çıkan satırlardan içeriği çıkarmak olacaktır:

:v/.*name="\v([^"]+).*/d|%s//\1

Standart grep

Bu araçlara erişiminiz yoksa, herhangi bir nedenle, standart grep ile benzer bir şey elde edilebilir. Ancak, etrafa bakmadan, daha sonra biraz temizlik gerektirecektir:

grep -o 'name="[^"]*"' filename

Sonuçları kaydetme hakkında bir not

Yukarıdaki tüm komutlarda sonuçlar e-posta adresine gönderilecektir stdout. Bunları her zaman ekleyerek bir dosyaya borulayarak kaydedebileceğinizi unutmamak önemlidir:

> result

komutun sonuna kadar.


12
Lookarounds (GNU'da grep):grep -Po '.*name="\K.*?(?=".*)'
Dennis Williamson

@Dennis Williamson, harika. Cevabı buna göre güncelledim ama ikisini de bir .*kenara bıraktım , umarım bana kızmazsın. Sormak istiyorum, açgözlü olmayan eşleşmenin "dışında herhangi bir şeyden" daha faydası görüyor "musunuz? Bunu bir kavga olarak alma, sadece merak ediyorum ve bir normal ifade uzmanı değilim. Ayrıca, \Kipucu, gerçekten güzel. Teşekkürler Dennis.
sidyll

2
neden kızgın olayım? Olmadan .*yapabilirsiniz grep -Po '(?<=name=").*?(?=")'. \KSteno için de kullanılabilir, ama gerçekten sadece sola maç değişken uzunluk ise ihtiyaç duyulan. Bu gibi durumlarda, lookarounds kullanmanın nedeni oldukça açıktır. Açgözlü olmayan operasyonlar biraz daha düzgün görünüyor (buna [^"]*karşılık .*?ana karakteri tekrarlamak zorunda değilsiniz. Hız hakkında bilmiyorum. Bu, bağlama bağlı, sanırım. Umarım bu yardımcı olur.
Dennis Williamson

@Dennis Williamson: kesinlikle efendim, burada birçok yararlı bilgi var. Sanırım \K(üzerinde araştırma yaptıktan sonra) tutup kaldırmamın sebebi .*aynıydı: güzel görünmesini sağlamak (daha basit). Ve bir .*?yerden öğrendiğim "geleneksel yol" yerine kullanmayı hiç düşünmedim . Ama açgözlü olmayan burada gerçekten mantıklı. Teşekkürler Dennis, en iyi dileklerimle.
sidyll

Komutu açıklamak için +1. Normal ifadenin "[...]" bölümünü açıklamak için yanıtınızı güncellerseniz seviniriz.
lreeder


5

Perl kullanıyorsanız, XML: XML :: Simple , XML :: Twig veya XML :: LibXML'yi ayrıştırmak için bir modül indirin . Tekerleği yeniden icat etmeyin.


3
OP'nin verdiği örneğin iyi biçimlendirilmediğine dikkat edin ( <type="global"örneğin), bu nedenle XML çözümleyicilerin çoğu sadece şikayet eder ve ölür.
bvr

5

Bu amaçla, normal ifadeler yerine bir HTML ayrıştırıcı kullanılmalıdır. Aşağıdakileri kullanan bir Perl programı 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>

Çıktı

content_analyzer
content_analyzer2
content_analyzer_items

2

bunu yapabilir:

perl -ne 'if(m/name="(.*?)"/){ print $1 . "\n"; }'

2

İşte HTML tidy & xmlstarlet kullanan bir çözüm:

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

1

Oops, sed komutu elbette düzenli komuttan önce gelmeli:

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

0

Xml'nizin (veya genel olarak metninizin) yapısı sabitse, en kolay yol kullanmaktır cut. Özel durumunuz için:

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 '"'
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.