Генерируйте Древовидный Вывод от Определенного/Общего XML-файла в Bash

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

Это - часть 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

У меня есть первый GREPed использование строк, который имеет "Название меню" и затем распечатал Тесты между 'дисплеем ="' и '"справкой' и выпустил этот вывод:

Main Menu">
Broadband
Load and Save Profiles
xDSL Interface

Но то, что я хочу, является к 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
задан 29 July 2018 в 02:48

2 ответа

Адаптация одного из примеров из 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' в ударе получит Вас символ табуляции.
  • распечатайте только для элементов, которые имеют 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
ответ дан 1 December 2019 в 16:19

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

xidel ex.xml  \
  -e '//@display/concat(substring("------",1,count(ancestor::*)),">",.)'
  • substring("------",1,n) грязный способ создать строку с n "-"
0
ответ дан 1 December 2019 в 16:19

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

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