物欲投げ捨てblog

ぅぉぉぉぉぉ( ‘д‘⊂彡☆))[物欲]

xmllint_特定の文字列を持つノードの親ノードを取る方法

シェルでXMLの中身を使う機会があった。 ググれば基本的な使い方は出てくるけど、とあるテキストを含むノードと並んだノード、 つまり親ノードに登って違う子ノードを取りたい(分かりにくい)といった例がなかったのでメモ。

使用するコマンドはxmllint。Linuxでインストールすべきはlibxml2-utils。
(ubuntuの例:sudo apt install libxml2-utils)

XPathで指定して情報を持ってこれる。 基本的なところは、xmllintとxpathでググって把握できると思われる。

でも、座標を含む要素なんかでありがちな、<要素>以下に座標があったり、 親子構造になっていて<要素><要素>以下に座標があったり。 これらの座標全部取りたい…となると、すぐは分からなかった。

例えばこんなXMLだったとする。

$ cat a.xml
<?xml version="1.0"?>
<asdf>
  <youso>
    <abc>text1</abc>
    <zahyo>
      <x>111</x>
      <y>222</y>
    </zahyo>
  </youso>
  <youso>
    <abc>tigauText</abc>
      <zahyo>
        <x>555</x>
        <y>666</y>
      </zahyo>
    <youso>
      <abc>text1</abc>
      <zahyo>
        <x>333</x>
        <y>444</y>
      </zahyo>
    </youso>
  </youso>
</asdf>

階層関係なく、text1を含んで、並んで記載されたzahyoを取りたい。 でも違うテキストを含む(ここではtigauText)要素のzahyoはいらない。 そんなときのシェル例はこちら。

$ xmllint --xpath '//abc[text()="text1"]/parent::node()/zahyo/x' a.xml 
<x>111</x><x>333</x>

先頭の//abc で、全ての要素を取ってくる。 加えて、[text()=“text1”]で、テキストがtext1であるものに絞り込む。 そしてparent::node()で一つ上の要素に登って、取りたいデータ(zahyo)を取るという流れだ。

あとは 【Shell】XMLファイルから要素や属性を抽出する - 発熱するマイナー魂 を参考にして整形すると、こんな感じ。

$ xmllint --xpath '//abc[text()="text1"]/parent::node()/zahyo/x' a.xml | ¥
sed -e "s/<\/x>/<\/x>\n/g" | sed -e "s/<x>\(.*\)<\/x>/\1/"
111
333

同じようにyも取って、配列に入れ込んでx,yを参照すれば、ほしい座標郡が取れそう。

$ xmllint --xpath '//abc[text()="text1"]/parent::node()/zahyo/y' a.xml | ¥
sed -e "s/<\/y>/<\/y>\n/g" | sed -e "s/<y>\(.*\)<\/y>/\1/"
222
444

(情報抜け(xはあるけどyが無い)は未考慮なのでご注意を)