Jak ładnie wydrukować XML z linii poleceń?

526

Powiązane: Jak mogę ładnie wydrukować JSON w skrypcie powłoki (unix)?

Czy istnieje skrypt powłoki (unix) do formatowania XML w postaci czytelnej dla człowieka?

Zasadniczo chcę, aby przekształcić następujące:

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

... w coś takiego:

<root>
    <foo a="b">lorem</foo>
    <bar value="ipsum" />
</root>
svidgen
źródło
1
Aby był xmllintdostępny w systemach Debian, musisz zainstalować pakiet libxml2-utils( libxml2nie zapewnia tego narzędzia, przynajmniej nie w Debianie 5.0 „Lenny” i 6.0 „Squeeze”).
twonkeys,

Odpowiedzi:

908

libxml2-utils

To narzędzie zawiera libxml2-utils:

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

Perla XML::Twig

To polecenie jest dostarczane z XML :: Twig moduł, czasem xml-twig-toolspakiet:

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

xmlstarlet

To polecenie zawiera xmlstarlet:

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

tidy

Sprawdź tidypaczkę:

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

Pyton

Python xml.dom.minidommoże formatować XML (zarówno python2, jak i 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

Potrzebujesz saxon-lint:

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

saxon-HE

Potrzebujesz 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'
Gilles Quenot
źródło
Dobra, szybka odpowiedź. Pierwsza opcja wydaje się bardziej wszechobecna w nowoczesnych instalacjach * nix. Drobna uwaga; ale czy można go wywołać bez pracy przez plik pośredni? Tzn echo '<xml .. />' | xmllint --some-read-from-stdn-option?
svidgen
Pakiet jest libxml2-utilsw moim pięknym ubuntu.
franzlorenzon
1
Zauważ, że „cat data.xml | xmllint --format - | tee data.xml” nie działa. W moim systemie czasami działało to w przypadku małych plików, ale zawsze przycinało duże pliki. Jeśli naprawdę chcesz zrobić coś w miejscu czytać backreference.org/2011/01/29/in-place-editing-of-files
user1346466
1
Aby rozwiązać UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 805: ordinal not in range(128)w wersji python, chcesz zdefiniować 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
Zauważ, że tidy może również formatować xml bez elementu głównego . Jest to przydatne do formatowania za pomocą potoku, sekcji xml (np. Wyodrębnionych z dzienników). echo '<x></x><y></y>' | tidy -xml -iq
Marinos An
157

xmllint --format yourxmlfile.xml

xmllint to narzędzie XML wiersza polecenia, które jest zawarte w libxml2( http://xmlsoft.org/ ).

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

Uwaga: Jeśli nie masz libxml2zainstalowanego programu, możesz go zainstalować, wykonując następujące czynności:

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

System operacyjny Mac

Aby zainstalować to na MacOS z Homebrew, po prostu wykonaj: brew install libxml2

Git

Dostępne również w Git, jeśli chcesz kod: git clone git://git.gnome.org/libxml2

crmpicco
źródło
4
Odpowiedź sputnick zawiera te informacje, ale odpowiedź crmpicco jest najbardziej użyteczną odpowiedzią na ogólne pytanie o to, jak ładnie wydrukować XML.
Seth Difley
2
możemy zapisać to sformatowane wyjście xml do innego pliku xml i użyć go .. np. xmllint - sformatuj swój plik xml.xml >> new-file.xml
LearnToLive
2
W systemie Ubuntu 16.04 można użyć następujących opcji:sudo apt-get install libxml2-utils
Melle
Działa to również w systemie Windows; gitdo pobrania dla systemu Windows instaluje nawet najnowszą wersję xmllint. Przykład:"C:\Program Files\Git\usr\bin\xmllint.exe" --format [email protected] > [email protected]
Jeroen Wiert Pluimers
41

Możesz także użyć tidy , który może wymagać instalacji w pierwszej kolejności (np. Na Ubuntu: sudo apt-get install tidy).

W tym celu wydasz coś takiego:

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

Uwaga: ma wiele dodatkowych flag czytelności, ale zawijanie słów jest nieco denerwujące, aby je rozplątać ( http://tidy.sourceforge.net/docs/quickref.html ).

matanster
źródło
1
Pomocne, ponieważ nie mogłem zmusić xmllint do dodania podziałów wierszy do pliku XML w jednym wierszu. Dzięki!
xlttj
tidyteż działa dobrze dla mnie. W przeciwieństwie do hxnormalizetego zrobione faktycznie zamyka <body>tag.
Sridhar Sarnobat
9
BTW, oto kilka opcji, które mają znaleźć przydatne: tidy --indent yes --indent-spaces 4 --indent-attributes yes --wrap-attributes yes --input-xml yes --output-xml yes < InFile.xml > OutFile.xml.
Victor Yarema,
2
Świetna wskazówka @VictorYarema. Połączyłem go z pygmentacją i dodałem do mojego .bashrc: alias prettyxml='tidy --indent yes --indent-spaces 4 --indent-attributes yes --wrap-attributes yes --input-xml yes --output-xml yes | pygmentize -l xml' a potem mogęcurl url | prettyxml
Net Wolf
13

Nie wspomniałeś o pliku, więc zakładam, że chcesz podać ciąg XML jako standardowe wejście w wierszu poleceń. W takim przypadku wykonaj następujące czynności:

$ echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' | xmllint --format -
David
źródło
12

Bez instalowania czegokolwiek na macOS / większość Uniksa.

Posługiwać się tidy

cat filename.xml | tidy -xml -iq

Przekierowanie przeglądania pliku z cat do uporządkowania z określeniem typu pliku xml i do wcięcia podczas cichego wyświetlania spowoduje usunięcie błędu. JSON współpracuje również z -json.

jasonleonhard
źródło
1
Nie trzeba się catkrok: tidy -xml -iq filename.xml. Możesz nawet zrobić, tidy -xml -iq filename.xmlużywając -mopcji zmodyfikowania oryginalnego pliku ...
Janniks
10

xmllint obsługuje formatowanie w miejscu :

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

Jak napisał Daniel Veillard:

Myślę, że xmllint -o tst.xml --format tst.xml powinno być bezpieczne, ponieważ parser w pełni załaduje dane wejściowe do drzewa przed otwarciem danych wyjściowych w celu serializacji.

Poziom wcięcia jest kontrolowany przez XMLLINT_INDENTzmienną środowiskową, która domyślnie ma 2 spacje. Przykład zmiany wcięcia na 4 spacje:

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

Może brakować --recoveropcji, gdy dokumenty XML są uszkodzone. Lub wypróbuj słaby parser HTML ze ścisłym wyjściem XML:

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

--nsclean, --nonet, --nocdata, --noblanksItd mogą być użyteczne. Przeczytaj stronę podręcznika man.

apt-get install libxml2-utils
apt-cyg install libxml2
brew install libxml2
gavenkoa
źródło
2

To zajęło mi wieczność, aby znaleźć coś, co działa na moim komputerze Mac. Oto, co zadziałało dla mnie:

brew install xmlformat
cat unformatted.html | xmlformat
Sridhar Sarnobat
źródło
1
Moja odpowiedź powyżej działa na komputerze Mac
Jasonleonhard
1

Chciałbym dodać czyste rozwiązanie Bash, ponieważ nie jest to „trudne” po prostu zrobić to ręcznie, a czasami nie chcesz instalować dodatkowego narzędzia do wykonania tego zadania.

#!/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

Wklej go do pliku skryptu i potokuj w pliku xml. Zakłada się, że xml jest w jednym wierszu i nigdzie nie ma żadnych dodatkowych spacji. Można łatwo dodać dodatkowe \s*do wyrażeń regularnych, aby to naprawić.

leondepeon
źródło