Генерировать вывод дерева из конкретного / общего XML-файла в Bash

Я пытаюсь сгенерировать дерево из файла XML в Bash.

Это часть файла XML:

<ke3600-menu-file language="en" display="English" index="1"> <version major="0" minor="1" patch="0"/> <locale name="en_EN" timezone="CET-1CEST,M3.5.0,M10.5.0/3"/> <menu name="main_menu" display="Main Menu"> <menu name="broadband" display="Broadband" help="100_help_broadband"> <onenter proc="activateGfast"/> <menu name="load_save_profiles" display="Load and Save Profiles" help="601_help_profiles"> <application name="load_profiles" display="Load Profile"/> <application name="save_profiles" display="Save Profile"/> <application name="remove_profiles" display="Delete Profile"/> </menu> <parameter type="list" name="wanInterface" display="Interface" help="101_help_wanInterface"> <value id="0" name="ifDSL" display="xDSL"/> <value id="1" name="ifSFP" display="SFP"/> <value id="2" name="ifETH" display="Ethernet"/> <!-- <value i d="3" name="ifWLAN" display="WLAN"/> --> <value id="4" name="ifSHDSL" display="SHDSL"/> </parameter> <menu name="xdslinterface" display="xDSL Interface" help="200_help_xDslInterface"> <parameter type="list" name="annex" display="xDSL Mode" help="201_help_xdslModi"> <value id="0" name="xdsl_line_annex_a" display="Annex A/M"/> <value id="1" name="xdsl_line_annex_b" display="Annex B/J"/> </parameter> <application name="xdsl_macAddr" display="MAC Address" caption="MAC Address"> <param type="string" name="xdsl_mac_addr" display="MAC Address" length="25"/> </application> <parameter type="list" name="xdsl_vectoring_mode" display="Vectoring Mode" help="202_help_Vectoring"> <value id="0" name="xdsl_vectoring_mode_disabled" display="Disabled"/> <value id="1" name="xdsl_vectoring_mode_enabled" display="Enabled"/> <value id="2" name="xdsl_vectoring_mode_friendly" display="Friendly"/> </parameter> <parameter type="list" name="xdsl_gfast_mode" display="G.FAST"> <value id="0" name="xdsl_gfast_mode_disabled" display="Disabled"/> <value id="1" name="xdsl_gfast_mode_enabled" display="Enabled"/> </parameter> <parameter type="list" name="resync" display="Auto Re-Sync"> <value id="1" name="re_sync_on" display="On"/> <value id="0" name="re_sync_off" display="Off"/> </parameter> <parameter type="list" name="seamlessRateAdaption" display="Seamless Rate Adaption"> <value id="0" name="sra_off" display="Off"/> <value id="1" name="sra_on" display="On"/> </parameter> <parameter type="list" name="xdsl_ginp" display="G.INP"> <value id="0" name="xdsl_ginp_none" display="Off"/> <value id="1" name="xdsl_ginp_down" display="Down"/> <value id="2" name="xdsl_ginp_up" display="Up"/> <value id="3" name="xdsl_ginp_both" display="Down/Up"/> </parameter> <parameter type="list" name="power_cut_back" display="Power Cut Back"> <value id="0" name="power_cut_back_off" display="Off"/> <value id="1" name="power_cut_back_on" display="On"/> </parameter> <parameter type="list" name="atmType" display="ATM Type"> <value id="0" name="atm_type_aal5" display="AAL5"/> <value id="1" name="atm_type_aal0pkt" display="AAL0PKT"/> <value id="2" name="atm_type_aal0cell" display="AAL0CELL"/> </parameter> <parameter type="list" name="voip_atmType" display="Voip Interface ATM Type"> <value id="0" name="voip_atm_type_aal5" display="AAL5"/> <value id="1" name="voip_atm_type_aal0pkt" display="AAL0PKT"/> <value id="2" name="voip_atm_type_aal0cell" display="AAL0CELL"/> </parameter> <parameter type="list" name="iptv_atmType" display="Iptv Interface ATM Type"> <value id="0" name="iptv_atm_type_aal5" display="AAL5"/> <value id="1" name="iptv_atm_type_aal0pkt" display="AAL0PKT"/> <value id="2" name="iptv_atm_type_aal0cell" display="AAL0CELL"/> </parameter> </menu> <menu name="ethernetinterface" display="Ethernet Interface" help="300_help_ethernetInterface"> <parameter type="list" name="ethInterfaceAuto" display="Autonegotiation"> <value id="1" name="ethInterfaceAutoOn" display="On"/> <value id="0" name="ethInterfaceAutoOff" display="Off"/> </parameter> <parameter type="list" name="ethInterfaceSpeed" display="Speed"> <value id="2" name="ethIf1000" display="1000MBit/s"/> <value id="1" name="ethIf100" display="100MBit/s"/> <value id="0" name="ethIf10" display="10MBit/s"/> </parameter> <parameter type="list" name="ethInterfaceDuplexMode" display="Duplex Mode"> <value id="1" name="ethIfFull" display="Full"/> <value id="0" name="ethIfHalf" display="Half"/> </parameter> </menu>

Я попытался использовать CAT и GREP и AWK:

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

Я сначала GREPed, используя строки, которые имеют «Имя меню», а затем распечатал тесты между «display =» и «help» и вышел с этим выходом:

Main Menu"> Broadband Load and Save Profiles xDSL Interface

Но я хочу, чтобы Grep все строки, которые имеют «Имя меню», «Тип параметра», «Имя приложения» и «Идентификатор значения» и печатают их отображаемое имя в дереве вывод. Я не уверен, как я могу Grep несколько значений из нескольких строк и печатать определенную строку из него.

Затем я увидел, что это сравнительно просто сделать с помощью инструмента анализа XML. Поэтому я попытался с XMLStarlet:

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

Используя эту команду, я нашел следующий вывод:

ke3600-menu-file -> ke3600-menu-file ke3600-menu-file -> version ke3600-menu-file -> locale ke3600-menu-file -> menu 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 -> Interface -> Ethernet Interface -> Terminal Mode -> Telephony -> Interface -> On -> Off -> Copper Test

Или следующее:

Main Menu -> Broadband --> Load and Save Profiles ---> Load Profile ---> Save Profile ---> Delete Profile --> Interface --> Ethernet Interface --> Terminal Mode -> Telephony --> Interface ---> On ---> Off -> Copper Test

Моя цель - сделать выход максимально приближенным к нему. Может ли кто-нибудь предложить мне, как я должен продолжить это?

2
задан 25 August 2017 в 11:39

6 ответов

Адаптация одного из примеров из документов xmlstarlet:

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 с помощью xml sel (расширенные выражения 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, поэтому @display вместо name() печатать его только для последнего элемента. У нас уже есть тест для печати . для всех, кроме последнего элемента, поэтому его легко инвертировать. напечатайте вкладки с отступом (мы можем сделать это после каждого элемента, он просто оставит скрытую, невидимую вкладку), так что просто -o $'\t'. $'\t' в bash вы получите символ табуляции. напечатайте только для элементов, которые имеют атрибут display, поэтому -i '@display'

Я сделал отступ указанной выше команды, чтобы сделать поток более четким.

Выход, который я получаю: [!d13 ]

$ 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
4
ответ дан 22 May 2018 в 19:05
  • 1
    Привет, Муру, у меня есть вопрос. Если я хочу добавить счетчик вместо вкладки, как мне это сделать? Я попытался добавить счетчик вместо вкладки здесь: -o $'\t' -b, но он просто добавляет значение (как и должно), и я не могу понять, как я должен печатать количество вкладок вместо вкладок. – Russo 1 September 2017 в 11:21
  • 2
    @Russo Я уверен, что xmlstarlet, вероятно, имеет какой-то способ сделать это, но на данный момент, возможно, использовать awk для обработки вывода: xmlstarlet sel -T -t -m '//*' -i '@display' -m 'ancestor::*' -o $'\t' -b -o '-> ' -v '@display' -n foo.xml | awk -F '\t' '{print NF,$NF}' – muru 1 September 2017 в 12:04
  • 3
    Да, это прекрасно работает. Не обязательно использовать xmlstarlet. Спасибо. – Russo 1 September 2017 в 12:20
  • 4
    @ Muru, очень здорово !! – JJoao 6 September 2017 в 15:58

Адаптация одного из примеров из документов xmlstarlet:

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 с помощью xml sel (расширенные выражения 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, поэтому @display вместо name() печатать его только для последнего элемента. У нас уже есть тест для печати . для всех, кроме последнего элемента, поэтому его легко инвертировать. напечатайте вкладки с отступом (мы можем сделать это после каждого элемента, он просто оставит скрытую, невидимую вкладку), так что просто -o $'\t'. $'\t' в bash вы получите символ табуляции. напечатайте только для элементов, которые имеют атрибут 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
4
ответ дан 18 July 2018 в 07:56

Адаптация одного из примеров из документов xmlstarlet:

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 с помощью xml sel (расширенные выражения 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, поэтому @display вместо name() печатать его только для последнего элемента. У нас уже есть тест для печати . для всех, кроме последнего элемента, поэтому его легко инвертировать. напечатайте вкладки с отступом (мы можем сделать это после каждого элемента, он просто оставит скрытую, невидимую вкладку), так что просто -o $'\t'. $'\t' в bash вы получите символ табуляции. напечатайте только для элементов, которые имеют атрибут 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
4
ответ дан 24 July 2018 в 18:56

(Если не установлено, установите xidel)

xidel ex.xml  \
  -e '//@display/concat(substring("------",1,count(ancestor::*)),">",.)'
substring("------",1,n) является грязным способом построения строки с n "-"
0
ответ дан 22 May 2018 в 19:05

(Если не установлено, установите xidel)

xidel ex.xml \ -e '//@display/concat(substring("------",1,count(ancestor::*)),">",.)' substring("------",1,n) является грязным способом построения строки с n "-"
0
ответ дан 18 July 2018 в 07:56

(Если не установлено, установите xidel)

xidel ex.xml \ -e '//@display/concat(substring("------",1,count(ancestor::*)),">",.)' substring("------",1,n) является грязным способом построения строки с n "-"
0
ответ дан 24 July 2018 в 18:56

Другие вопросы по тегам:

Похожие вопросы: