grep、regex、またはperlを使用してパターンに従って文字列を抽出する方法


90

次のようなファイルがあります。

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

私は続く引用符で何かを抽出する必要がありname=、すなわち、content_analyzercontent_analyzer2content_analyzer_items

私はこれをLinuxボックスで行っているので、sed、perl、grep、またはbashを使用したソリューションで問題ありません。


5
恥ずかしがる必要はありません、ここにようこそ!
ブノワ

8
私はそれがリンクするには間違っていないだろうと感じてstackoverflow.com/questions/1732348/...
クリストファーHammarström

有益なコメントをありがとうございました。XMLが正しくフォーマットされていないことをお詫びします。簡略化のためにいくつかのタグを削除しました。
ラングラー2011

回答:


167

結果にコンテンツを含めずにコンテンツを一致させる必要があるため(一致する必要がありますがname=" 、目的の結果の一部ではありません)、何らかの形式のゼロ幅の一致またはグループキャプチャが必要です。これは、次のツールを使用して簡単に実行できます。

Perl

Perlを使用すると、n オプションを使用して1行ずつループし、キャプチャグループのコンテンツが一致する場合はそれを出力できます。

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

GNU grep

GNU grepなどの改良版のgrepがある場合は、-Pオプションを利用できる場合があり ます。このオプションは、Perlのような正規表現を有効にし\K、略記の後ろ書きを使用できるようにします。一致位置がリセットされるため、幅がゼロになる前はすべてリセットされます。

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

このo オプションを使用すると、grepは行全体ではなく、一致したテキストのみを出力します。

Vim-テキストエディタ

もう1つの方法は、テキストエディタを直接使用することです。Vimを使用する場合、これを実現するさまざまな方法の1つは、行を削除せずに行を削除 name=し、結果の行からコンテンツを抽出することです。

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

標準のgrep

これらのツールにアクセスできない場合は、何らかの理由で、標準のgrepを使用して同様のことが実現できます。ただし、周りを見渡さないと、後でクリーンアップが必要になります。

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

結果の保存に関する注意

上記のすべてのコマンドで、結果はに送信されstdoutます。以下を追加してファイルにパイプすることで、いつでも保存できることを覚えておくことが重要です。

> result

コマンドの最後まで。


12
ルックアラウンド(GNUの場合grep):grep -Po '.*name="\K.*?(?=".*)'
通知があるまで一時停止します。

@デニスウィリアムソン、素晴らしい。私はそれに応じて答えを更新しましたが、両方を.*脇に置いて、あなたが私に腹を立てないことを願っています。質問したいのですが、「以外のもの」よりも貪欲でない試合のメリットはあります"か?これを戦いと見なさないでください。私はただ興味があり、正規表現の専門家ではありません。また、\Kヒント、本当にいいです。デニスに感謝します。
sidyll 2011

2
なぜ私は怒っているのでしょうか?がなくても.*、できますgrep -Po '(?<=name=").*?(?=")'\K速記のために使用することができますが、それは実際にはその左にあるマッチが可変長である場合に必要です。このような場合、ルックアラウンドを使用する理由はかなり明白です。Ungreedy操作が(少しすっきり見える[^"]*.*?して、あなたはアンカー文字を繰り返す必要はありません、私はスピードのことは知らない状況に多くを依存していることを、私が思うに、私はそれが参考に願っています。。。。
一時停止追って通知があるまで。

@Dennis Williamson:確かに、ここにはたくさんの役立つ情報があります。私が\K(それを研究した後)保持して削除した理由は同じだと思います.*:それをきれいに(よりシンプルに)見せてください。そして.*?、どこかで学んだ「伝統的な方法」の代わりに使うことを考えたことはありません。しかし、ここで貪欲でないことは本当に理にかなっています。デニスに感謝します。
sidyll 2011

コマンドを説明するための+1。正規表現の「[...]」の部分を説明するために回答を更新していただければ幸いです。
lreeder 2014年


5

Perlを使用している場合は、XMLを解析するモジュールをダウンロードします:XML :: SimpleXML :: Twig、またはXML :: LibXML。車輪の再発明をしないでください。


3
OPが示した例は(<type="global"たとえば)整形式ではないため、ほとんどのXMLパーサーは文句を言って死ぬことに注意してください。
bvr 2011

5

この目的には、正規表現ではなくHTMLパーサーを使用する必要があります。以下を利用するPerlプログラムHTML::TreeBuilder

プログラム

#!/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>

出力

content_analyzer
content_analyzer2
content_analyzer_items

2

これはそれを行うことができます:

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

2

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

1

もちろん、sedコマンドはtidyコマンドの前に置く必要があります。

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(または一般的なテキスト)の構造が修正されている場合、最も簡単な方法はを使用することcutです。特定のケースの場合:

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 '"'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.