Как красиво распечатать XML из командной строки?

528

Связанный: Как я могу красиво напечатать JSON в (unix) сценарии оболочки?

Существует ли (unix) сценарий оболочки для форматирования XML в удобочитаемой форме?

По сути, я хочу преобразовать следующее:

<root><foo a="b">lorem</foo><bar value="ipsum" /></root>

... в нечто вроде этого:

<root>
    <foo a="b">lorem</foo>
    <bar value="ipsum" />
</root>
svidgen
источник
1
Чтобы иметь xmllintдоступ к системам Debian, вам нужно установить пакет libxml2-utils( libxml2не предоставляет этот инструмент, по крайней мере, в Debian 5.0 "Lenny" и 6.0 "Squeeze").
Twonkeys

Ответы:

909

libxml2-utils

Эта утилита поставляется с libxml2-utils:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    xmllint --format -

в Perl XML::Twig

Эта команда поставляется с XML :: Twig модуль, иногда xml-twig-toolsпакет:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    xml_pp

xmlstarlet

Эта команда поставляется с xmlstarlet:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    xmlstarlet format --indent-tab

tidy

Проверьте tidyпакет:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    tidy -xml -i -

питон

Python xml.dom.minidomможет форматировать XML (как python2, так и python3):

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    python -c 'import sys;import xml.dom.minidom;s=sys.stdin.read();print(xml.dom.minidom.parseString(s).toprettyxml())'

saxon-lint

Вам нужно saxon-lint:

echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    saxon-lint --indent --xpath '/' -

saxon-HE

Вам нужно saxon-HE:

 echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' |
    java -cp /usr/share/java/saxon/saxon9he.jar net.sf.saxon.Query \
    -s:- -qs:/ '!indent=yes'
Жиль Квено
источник
Хороший, быстрый ответ. Первый вариант кажется более распространенным в современных установках * nix. Незначительный момент; но можно ли его вызвать, не работая через промежуточный файл? То есть echo '<xml .. />' | xmllint --some-read-from-stdn-option?
svidgen
Пакет libxml2-utilsв моей красивой убунту.
franzlorenzon
1
Обратите внимание, что «cat data.xml | xmllint --format - | tee data.xml» не работает. В моей системе это иногда работало для маленьких файлов, но всегда усекало огромные файлы. Если вы действительно хотите сделать что - нибудь на месте прочитать backreference.org/2011/01/29/in-place-editing-of-files
user1346466
1
Чтобы решить UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 805: ordinal not in range(128)в Python версии вы хотите определить PYTHONIOENCODING="UTF-8":cat some.xml | PYTHONIOENCODING="UTF-8" python -c 'import sys;import xml.dom.minidom;s=sys.stdin.read();print xml.dom.minidom.parseString(s).toprettyxml()' > pretty.xml
FelikZ
1
Обратите внимание, что Tidy также может форматировать XML без корневого элемента . Это полезно для форматирования через канал, разделы XML (например, извлеченные из журналов). echo '<x></x><y></y>' | tidy -xml -iq
Маринос
157

xmllint --format yourxmlfile.xml

xmllint - это инструмент XML для командной строки, который включен в libxml2( http://xmlsoft.org/ ).

================================================

Примечание. Если у вас нет libxml2установленного программного обеспечения, вы можете установить его, выполнив следующие действия:

CentOS

cd /tmp
wget ftp://xmlsoft.org/libxml2/libxml2-2.8.0.tar.gz
tar xzf libxml2-2.8.0.tar.gz
cd libxml2-2.8.0/
./configure
make
sudo make install
cd

Ubuntu

sudo apt-get install libxml2-utils

Cygwin

apt-cyg install libxml2

MacOS

Чтобы установить это на MacOS с Homebrew, просто сделайте: brew install libxml2

Гит

Также доступно на Git, если вы хотите код: git clone git://git.gnome.org/libxml2

crmpicco
источник
4
Ответ sputnick содержит эту информацию, но ответ crmpicco является наиболее полезным ответом на общий вопрос о том, как правильно печатать XML.
Сет Дифли
2
мы можем записать этот форматированный вывод xml в другой файл xml и использовать его .. например, xmllint --format yourxmlfile.xml >> new-file.xml
LearnToLive
2
В Ubuntu 16.04 вы можете использовать следующее:sudo apt-get install libxml2-utils
Melle
Это работает и в Windows; gitдля загрузки Windows даже устанавливает последнюю версию xmllint. Пример:"C:\Program Files\Git\usr\bin\xmllint.exe" --format QCScaper.test@borland.com.cds.xml > QCScaper.test@borland.com.pretty-printed.cds.xml
Йерун Wiert Pluimers
41

Вы также можете использовать tidy , который может потребоваться сначала установить (например, в Ubuntu: sudo apt-get install tidy).

Для этого вы должны выполнить что-то вроде следующего:

tidy -xml -i your-file.xml > output.xml

Примечание: имеет много дополнительных флагов читабельности, но поведение переноса слов немного раздражает ( http://tidy.sourceforge.net/docs/quickref.html ).

matanster
источник
1
Полезно, потому что я не мог заставить xmllint добавлять разрывы строк в однострочный XML-файл. Спасибо!
xlttj
tidyхорошо работает для меня тоже. В отличие от hxnormalizeэтого, на самом деле это закрывает <body>тег.
Шридхар Сарнобат
9
Кстати, вот несколько вариантов , которые я нашел полезным: tidy --indent yes --indent-spaces 4 --indent-attributes yes --wrap-attributes yes --input-xml yes --output-xml yes < InFile.xml > OutFile.xml.
Виктор Ярема
2
Отличный совет @VictorYarema. Я объединил его с pygmentize и добавил в мой .bashrc: alias prettyxml='tidy --indent yes --indent-spaces 4 --indent-attributes yes --wrap-attributes yes --input-xml yes --output-xml yes | pygmentize -l xml' и затем могуcurl url | prettyxml
Net Wolf
13

Вы не упомянули файл, поэтому я предполагаю, что вы хотите предоставить строку XML в качестве стандартного ввода в командной строке. В этом случае сделайте следующее:

$ echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' | xmllint --format -
Дэвид
источник
12

Без установки чего-либо на macOS / большинство Unix.

использование tidy

cat filename.xml | tidy -xml -iq

Перенаправление просмотра файла с помощью команды cat на tidy с указанием типа файла xml и на отступ при тихом выводе будет подавлять вывод ошибок. JSON также работает с -json.

jasonleonhard
источник
1
Вам не нужен catшаг tidy -xml -iq filename.xml. Кроме того, вы даже можете tidy -xml -iq filename.xmlиспользовать -mопцию для изменения исходного файла ...
Янв
10

xmllint поддерживает форматирование на месте :

for f in *.xml; do xmllint -o $f --format $f; done

Как писал Даниэль Вейяр:

Я думаю, что это xmllint -o tst.xml --format tst.xml должно быть безопасно, так как парсер полностью загрузит вход в дерево перед тем, как открыть выход для его сериализации.

Уровень отступа контролируется XMLLINT_INDENTпеременной окружения, которая по умолчанию имеет 2 пробела. Пример, как изменить отступ на 4 пробела:

XMLLINT_INDENT='    '  xmllint -o out.xml --format in.xml

Возможно, вам не хватает --recoverопции, когда ваши XML-документы повреждены. Или попробуйте слабый анализатор HTML со строгим выводом XML:

xmllint --html --xmlout <in.xml >out.xml

--nsclean, --nonet, --nocdata, И --noblanksт.д. , может быть полезным. Прочитайте справочную страницу.

apt-get install libxml2-utils
apt-cyg install libxml2
brew install libxml2
gavenkoa
источник
2

Это заняло у меня целую вечность, чтобы найти то, что работает на моем Mac. Вот что сработало для меня:

brew install xmlformat
cat unformatted.html | xmlformat
Шридхар Сарнобат
источник
1
Мой ответ выше работает на Mac
Jasonleonhard
1

Я хотел бы добавить чистое решение Bash, так как это не так просто сделать вручную, и иногда вам не нужно устанавливать дополнительный инструмент для выполнения этой работы.

#!/bin/bash

declare -i currentIndent=0
declare -i nextIncrement=0
while read -r line ; do
  currentIndent+=$nextIncrement
  nextIncrement=0
  if [[ "$line" == "</"* ]]; then # line contains a closer, just decrease the indent
    currentIndent+=-1
  else
    dirtyStartTag="${line%%>*}"
    dirtyTagName="${dirtyStartTag%% *}"
    tagName="${dirtyTagName//</}"
    # increase indent unless line contains closing tag or closes itself
    if [[ ! "$line" =~ "</$tagName>" && ! "$line" == *"/>"  ]]; then
      nextIncrement+=1
    fi
  fi

  # print with indent
  printf "%*s%s" $(( $currentIndent * 2 )) # print spaces for the indent count
  echo $line
done <<< "$(cat - | sed 's/></>\n</g')" # separate >< with a newline

Вставьте его в файл сценария и отправьте в xml. Это предполагает, что все xml находятся в одной строке, и нигде нет лишних пробелов. Можно легко добавить некоторые дополнительные \s*к регулярным выражениям, чтобы исправить это.

leondepeon
источник