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
できる限りそれに近い出力を得ることを目指しています。誰も私がこれをどのように進めるべきかを提案できますか?
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
(まだインストールされていない場合は、xidelをインストールします)
xidel ex.xml \
-e '//@display/concat(substring("------",1,count(ancestor::*)),">",.)'
substring("------",1,n)
は、n個の「-」で文字列を構築する汚い方法です