マイナー・マイナー

隠れた名作の発掘が生きがい。

【Shell】XMLファイルから要素や属性を抽出する


スポンサードリンク

XMLファイルから特定の要素や属性を抽出して整形する方法をまとめました。Shellのgrepsed、xmllintコマンドを組み合わせて、要素や属性を取得します。


f:id:yosinoo:20160112222300p:plain


下記ファイル(samle.xml)を考えます。
※実際には要素間に改行とスペースが入っていないファイルです。

<?xml version="1.0" encoding="utf-8"?>
<members>
  <member id="1">
    <name>hoge</name>
    <age>21</age>
  </member>
  <member id="2">
    <name>piyo</name>
    <age>34</age>
  </member>
  <member id="3">
    <name>foo</name>
    <age>25</age>
  </member>
  <member id="4">
    <name>bar</name>
    <age>18</age>
  </member>
  <member id="5">
    <name>fuga</name>
    <age>100</age>
  </member>
</members>


grepsedで抽出
XMLの要素は、抽出対象の要素に子要素を含んでいないのであれば(例のnameやage)、oオプションを指定したgrepで取得できそうです。

$ grep -o "<name[^>]*>[^<]*</name>" sample.xml
<name>hoge</name>
<name>piyo</name>
<name>foo</name>
<name>bar</name>
<name>fuga</name>


要素の内容はパイプでつないでsed等で抽出できます。

$ grep -o "<name[^>]*>[^<]*</name>" sample.xml | sed -e "s/<name>\(.*\)<\/name>/\1/"
hoge
piyo
foo
bar
fuga


抽出対象の要素に子要素を含んでいるのであれば(例のmember)、例えば要素の前に改行を埋め込み、行単位でgrepするように仕掛ければ取得できそうです。

$ sed -e "s/<member /\n<member /g" sample.xml | grep -o "^<member [^>]*>.*</member>"
<member id="1"><name>hoge</name><age>21</age></member>
<member id="2"><name>piyo</name><age>34</age></member>
<member id="3"><name>foo</name><age>25</age></member>
<member id="4"><name>bar</name><age>18</age></member>
<member id="5"><name>fuga</name><age>100</age></member>


属性はさらにパイプしてsedで取得。

$ sed -e "s/<member /\n<member /g" sample.xml | grep -o "^<member [^>]*>.*</member>" | sed -e "s/^<member [^>]*id=\"\([^\"]*\)\".*$/\1/g"
1
2
3
4
5

工夫の余地はたくさんありそうです。


xmllintとsedで抽出
xmllintではXPathが利用できるので、これを利用して抽出します。

$ xmllint --xpath "/members/member/name" sample.xml
<name>hoge</name><name>piyo</name><name>foo</name><name>bar</name><name>fuga</name>


要素の内容はtext()で取り出します。

$ xmllint --xpath "/members/member/name/text()" sample.xml
hogepiyofoobarfuga


改行無しの出力となるので、終了タグの直後に改行を入れる等して見やすくします。(出力結果を見やすく整形するオプションが見当たらなかった)

$ xmllint --xpath "/members/member/name" sample.xml | sed -e "s/<\/name>/<\/name>\n/g"
<name>hoge</name>
<name>piyo</name>
<name>foo</name>
<name>bar</name>
<name>fuga</name>


要素の内容の抽出。

$ xmllint --xpath "/members/member/name" sample.xml | sed -e "s/<\/name>/<\/name>\n/g" | sed -e "s/<name>\(.*\)<\/name>/\1/"
hoge
piyo
foo
bar
fuga


抽出対象の要素に子要素を含んでいたとしても、XPathならばシンプルな記述で抽出できそうです。

$ xmllint --xpath "/members/member" sample.xml | sed -e "s/<\/member>/<\/member>\n/g"
<member id="1"><name>hoge</name><age>21</age></member>
<member id="2"><name>piyo</name><age>34</age></member>
<member id="3"><name>foo</name><age>25</age></member>
<member id="4"><name>bar</name><age>18</age></member>
<member id="5"><name>fuga</name><age>100</age></member>


属性の抽出は、XPathに@を使います。

$ xmllint --xpath "/members/member/@id" sample.xml
 id="1" id="2" id="3" id="4" id="5"


空白区切りの出力のようなので、それを利用して整形できそうです。

$ xmllint --xpath "/members/member/@id" sample.xml | sed -e "s/ id=\"\([^\"]*\)\"/\1\n/g"
1
2
3
4
5