web-dev-qa-db-ja.com

Bashの特定/一般XMLファイルからツリー出力を生成する

BashでXMLファイルからツリーを生成しようとしています。

これはXMLファイルの一部です。

  <menu name="main_menu" display="Main Menu">
      <application name="load_profiles" display="Load Profile"/>
      <application name="save_profiles" display="Save Profile"/>
      <application name="remove_profiles" display="Delete Profile"/>
      </menu>

私はCATとGREPとAWKを使用しようとしました:

cat menu.xml | grep menu\ name | awk -v FS="(display=\"|\" help)" '{print $2}' > menulist.txt

「メニュー名」の行を使用して最初にGREPを実行し、次に「display = "」と「" help」の間にテストを出力して、次の出力を作成しました。

Main Menu">
Broadband
Load and Save Profiles
xDSL Interface

しかし、私が欲しいのは、「メニュー名」、「パラメータタイプ」、「アプリケーション名」、「値ID」を持つすべての行をGrepし、出力のようなツリーに表示名を印刷することです。複数の行から複数​​の値をGrepし、そこから特定の文字列を出力する方法がわかりません。

その後、XMLパーサーツールを使用してこれを行う方が比較的簡単であることがわかりました。だから私はXMLStarletで試しました:

xmlstarlet el menu.xml|awk -F'/' 'BEGIN{print "digraph{"}{print $(NF-1)" -> "$NF}END{print"}"}'> menumenutxt.txt

このコマンドを使用すると、次の出力が見つかりました。

menu -> menu
menu -> onenter
menu -> menu
menu -> application
menu -> application
menu -> application
menu -> parameter
parameter -> value
parameter -> value

それは間違いなく良くなり、私が望むものに近くなります。ただし、表示名は印刷されません。

私が印刷しようとしているのは次のようなものです:

 Main Menu -> 
           -> Broadband 
                        -> Load and Save Profiles
                                                  -> Load Profile
                                                  -> Save Profile
                                                  -> Delete Profile

または以下:

Main Menu 
-> Broadband 
--> Load and Save Profiles
---> Load Profile
---> Save Profile
---> Delete Profile

できる限りそれに近い出力を得ることを目指しています。誰も私がこれをどのように進めるべきかを提案できますか?

3
Russo

the xmlstarlet docs の例の1つを適合させる:

xmlstarlet sel -T -t -m '//*' \
    -i '@display' \
        -m 'ancestor-or-self::*' \
            -i '(position()=last())' \
                -o '-> ' -v '@display' -b \
            -o $'\t' -b \
        -n foo.xml

例は次のとおりです。

Xml selを使用したXML要素の構造の印刷(高度なXPath式とxml selコマンドの使用法)

xml sel -T -t -m '//*' \
-m 'ancestor-or-self::*' -v 'name()' -i 'not(position()=last())' -o . -b -b -n \
xml/structure.xml

結果出力:

a1
a1.a11
a1.a11.a111
a1.a11.a111.a1111
a1.a11.a112
a1.a11.a112.a1121
a1.a12
a1.a13
a1.a13.a131

ここから、変更する必要があるものは次のとおりです。

  • displayの代わりにname属性を出力するので、name()の代わりに@displayを出力します
  • 最後の要素についてのみ印刷します。最後の要素を除くすべての要素に対して.を印刷するためのテストが既にあるので、それを逆にするのは簡単です。
  • インデントするタブを印刷します(すべての要素の後に行うことができます。末尾の非表示のタブを残すだけです)。したがって、-o $'\t'だけです。 bashの$'\t'はタブ文字を取得します。
  • display属性を持つ要素に対してのみ印刷するため、-i '@display'

フローを明確にするために、上記のコマンドをインデントしました。

私が得る出力:

$ xmlstarlet sel -T -t -m '//*' -i '@display' -m 'ancestor-or-self::*' -i '(position()=last())' -o '-> ' -v '@display' -b -o $'\t' -b -n foo.xml
-> English
    -> Main Menu
        -> Broadband
            -> Load and Save Profiles
                -> Load Profile
                -> Save Profile
                -> Delete Profile
            -> Interface
                -> xDSL
                -> SFP
                -> Ethernet
                -> SHDSL
            -> xDSL Interface
                -> xDSL Mode
                    -> Annex A/M
                    -> Annex B/J
                -> MAC Address
                    -> MAC Address
                -> Vectoring Mode
                    -> Disabled
                    -> Enabled
                    -> Friendly
                -> G.FAST
                    -> Disabled
                    -> Enabled

少し考えてみると、次の方が簡単です。

xmlstarlet sel -T -t -m '//*' \
    -i '@display' \
        -m 'ancestor::*' \
            -o $'\t' -b \
        -o '-> ' -v '@display' -n foo.xml

ancestor::*の代わりにancestor-or-self::*を使用すると、タブを正しく簡単に印刷でき、最後の要素の余分なテストが不要になります。

同様の出力ですが、末尾のタブはありません:

-> English
    -> Main Menu
        -> Broadband
            -> Load and Save Profiles
                -> Load Profile
                -> Save Profile
                -> Delete Profile
            -> Interface
                -> xDSL
                -> SFP
                -> Ethernet
                -> SHDSL
            -> xDSL Interface
                -> xDSL Mode
                    -> Annex A/M
                    -> Annex B/J
                -> MAC Address
                    -> MAC Address
                -> Vectoring Mode
                    -> Disabled
                    -> Enabled
                    -> Friendly
                -> G.FAST
                    -> Disabled
                    -> Enabled
3
muru

(まだインストールされていない場合は、xidelをインストールします)

xidel ex.xml  \
  -e '//@display/concat(substring("------",1,count(ancestor::*)),">",.)'
  • substring("------",1,n)は、n個の「-」で文字列を構築する汚い方法です
0
user216043