2つのタグ/パターン間で最初のn回発生するsed結果セット

2つのタグ/パターン間で最初のn回発生するsed結果セット

大きなXMLファイルがあり、2つのタグ間で発生するすべてのイベントを取得します。

私がしたことは次のとおりです。

sed -n '/<tag>/,/<\/tag>/p' file.xml

最初のN項目のみを取得するようにフィルタリングする必要があります。 l paramを試しましたが、十分ではありません:(

それでは、すべての結果セットでN個の一致するイベントを取得する方法を知っている人はいますか?

例えば。以下はxmlファイルの内容です。

<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
</root>

 sed -n '/<tag>/,/<\/tag>/p' file.xml 

すべての要素を返します。

したがって、目標は、n = 2の場合、上位n個の一致パターン(要素は複数行)を取得するためにフィルタリングすることです。結果は次のようになります。

<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>

答え1

努力する:

xmllint --xpath '//tag[position()<=2]' file.xml

または:

xmlstarlet sel -t -c '//tag[position()<=2]' file.xml

または:

xmlstarlet sel -t -m '//tag[position()<=2]' -c . -n file.xml

これを使用したい場合は、sed次のようにできます。

sed -n '
  1{x;s/^/../;x;}; # initialise counter with two tokens
  /<tag>/,/<\/tag>/ {
    p; /<\/tag>/{
      x;s/.//;/./!q;x; # remove a token and quit if hold space empty
    }
  }' file.xml

つまり、スペアスペース表示する残りのカウンタで(ドット文字を使用)

答え2

これにはパーサーを使用する必要がありますが、ご存知のようにすべての要素を印刷するsed -n '/<tag>/,/<\/tag>/p' file.xmlため、すべての要素を取得します。このコマンドは、p入力に含まれる行と次の行の間のすべての<tag>行を指定することで機能します</tag>。これはほとんどすべての行を構成するので、p印刷するだけでは大きな違いはありません。次のことが目標に近づくことがあります。

sed -n '\|<tag>|{:n
    \|</tag>|!{N;bn}
    y|\n| |;p
}'

<tag>行のアドレスを指定し、その行を確認してください。</tag>終了文字列が含まれていない場合は、別の行が取得され、パターンスペースが含まれるまで繰り返されます<tag>.*</tag>[^\n]*$

次に、\nパターン空間のすべての改行文字を空白に変換します。

ここに戻ります:

sed -n '\|<tag>|{:n;\|</tag>|!{N;bn};y|\n| |;p}' <<\DATA
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
</root>
DATA

出力:

<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>
<tag>  <t1>john</t1>  <t2>john</t2>  <t3>john</t3> </tag>

これで、次のことができます。

sed -n '\|<tag>|{:n
    \|</tag>|!{N;bn}
    y|\n| |;p
}' ./file | 
sed 's|> |>\n|g;2q'

...それは私に次のものを与えます:

<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>
<tag>
 <t1>john</t1>
 <t2>john</t2>
 <t3>john</t3>
</tag>

答え3

私の考えでは、これはあなたが望むものです。

sed -n '/<tag>/,/<\/tag>/p' file.xml | head -10

次のコマンドを使用して<tag>最初の2行をインポートします。

$ sed -n '/^<tag>/p' file.xml | head -2
<tag><t1>john</t1></tag>
<tag><t1>john</t1></tag>

答え4

私が知っている限り、一致sedは常に貪欲です。つまり、その間の他のXMLオブジェクトを含む/<tag>/,/<\/tag>/最初のインスタンスから最後のインスタンスまで<tag>一致することです。<\tag>

あなたのバージョンが複数文字のレコード区切り文字をサポートしている場合は、awk次のことができます。

awk -v n=2 'BEGIN{RS="</tag>\n";ORS=RS} NR<=n'

しかし、はるかに強力なソリューションは、専用のXMLパーサーを使用することです。たとえば、Pythonを使用した非常に単純な実装です。minidom

#!/usr/bin/python

from xml.dom import minidom

xmldoc = minidom.parse('file.xml')
taglist = xmldoc.getElementsByTagName('tag')
for i in range(2) :
        print taglist[i].toxml()

関連情報