Friday, May 3, 2024
4
rated 0 times [  4] [ 0]  / answers: 1 / hits: 3144  / 2 Years ago, tue, august 9, 2022, 2:30:20

I am trying to generate a tree from a XML file in Bash.



This is a part of the XML file:



  <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>


I have tried to use CAT and GREP and AWK:



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


I have first GREPed using the lines that has "Menu Name" and then printed the Tests between 'display="' and '" help' and came out with this output:



Main Menu">
Broadband
Load and Save Profiles
xDSL Interface


But what I want is to Grep all the lines that has "Menu Name", "parameter type", "application name" and "value id" and print their display name in a tree like output. I am not sure how I can Grep multiple values from multiple lines and print specific string from it.



Then I have seen that it is comparatively easier to do this with a XML parser tool. So I have tried with XMLStarlet:



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


Using this command I have found the following output:



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


Which definitely looks better and closer to what I want. But it's not printing the display name.



What I am trying to print is something like this:



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


Or the following:



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


My aim to to get an output as close to it as possible. Can anyone suggest me how I should proceed with this?


More From » command-line

 Answers
7

Adapting one of the examples from the xmlstarlet docs:



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


The example is:




Print structure of XML element using xml sel (advanced XPath
expressions and xml sel command usage)



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


Result Output:



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



From here, the things we need to modify are:




  • print the display attribute instead of the name, so @display instead of name()

  • print it only for the last element. We already have the test for printing . for all but the last element, so it's easy to invert that.

  • print tabs to indent (we can do it after every element, it will just leave trailing, invisible tab), so just -o $' '. $' ' in bash will get you a tab character.

  • print only for elements which have the display attribute, so -i '@display'



I have indented the command above to make the flow clearer.



The output I get:



$ xmlstarlet sel -T -t -m '//*' -i '@display' -m 'ancestor-or-self::*' -i '(position()=last())' -o '-> ' -v '@display' -b -o $'	' -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





After thinking a bit, the following is simpler:



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


Using ancestor::* instead of ancestor-or-self::* makes printing the tabs correctly easier, and eliminates the extra test for last element.



Similar output, but without trailing tabs:



-> 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

[#10445] Tuesday, August 9, 2022, 2 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
piscen

Total Points: 134
Total Questions: 117
Total Answers: 133

Location: Indonesia
Member since Wed, Jul 7, 2021
3 Years ago
;