Comment imprimer assez XML à partir de la ligne de commande?

528

En relation: Comment puis-je joliment imprimer JSON dans un script shell (unix)?

Existe-t-il un script shell (unix) pour formater XML sous une forme lisible par l'homme?

Fondamentalement, je veux qu'il transforme les éléments suivants:

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

... en quelque chose comme ça:

<root>
    <foo a="b">lorem</foo>
    <bar value="ipsum" />
</root>
svidgen
la source
1
Pour être xmllintdisponible sur les systèmes Debian, vous devez installer le paquet libxml2-utils( libxml2ne fournit pas cet outil, du moins pas sur Debian 5.0 "Lenny" et 6.0 "Squeeze").
twonkeys

Réponses:

909

libxml2-utils

Cet utilitaire est livré avec libxml2-utils:

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

Perl's XML::Twig

Cette commande est livrée avec XML :: Twig module, parfois xml-twig-toolspackage:

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

xmlstarlet

Cette commande est livrée avec xmlstarlet:

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

tidy

Vérifiez le tidypackage:

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

Python

Python xml.dom.minidompeut formater XML (à la fois python2 et 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

Vous avez besoin de saxon-lint:

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

saxon-HE

Vous avez besoin de 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
la source
Bonne réponse rapide. La première option semble être plus omniprésente sur les installations modernes * nix. Un point mineur; mais peut-il être appelé sans passer par un fichier intermédiaire? -À- dire, echo '<xml .. />' | xmllint --some-read-from-stdn-option?
svidgen
Le paquet est libxml2-utilsdans ma belle Ubuntu.
franzlorenzon
1
Notez que "cat data.xml | xmllint --format - | tee data.xml" ne fonctionne pas. Sur mon système, cela fonctionnait parfois pour les petits fichiers, mais toujours les fichiers énormes tronqués. Si vous voulez vraiment faire quoi que ce soit en place, lisez backreference.org/2011/01/29/in-place-editing-of-files
user1346466
1
Pour résoudre UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 805: ordinal not in range(128)en version python, vous voulez définir 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
Notez que tidy peut également formater xml sans élément racine . Ceci est utile pour formater à travers un tube, des sections xml (par exemple extraites des journaux). echo '<x></x><y></y>' | tidy -xml -iq
Marinos
157

xmllint --format yourxmlfile.xml

xmllint est un outil XML en ligne de commande et est inclus dans libxml2( http://xmlsoft.org/ ).

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

Remarque: Si vous ne l'avez pas libxml2installé, vous pouvez l'installer en procédant comme suit:

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

Pour l'installer sur MacOS avec Homebrew, faites simplement: brew install libxml2

Git

Aussi disponible sur Git si vous voulez le code: git clone git://git.gnome.org/libxml2

crmpicco
la source
4
La réponse de sputnick contient ces informations, mais la réponse de crmpicco est la réponse la plus utile ici à la question générale sur la façon d'imprimer du XML.
Seth Difley
2
nous pouvons écrire cette sortie xml formatée dans un autre fichier xml et l'utiliser. Par exemple, xmllint --format yourxmlfile.xml >> new-file.xml
LearnToLive
2
Sur Ubuntu 16.04, vous pouvez utiliser les éléments suivants:sudo apt-get install libxml2-utils
Melle
Cela fonctionne aussi sur Windows; gitpour Windows, le téléchargement installe même une version récente de xmllint. Exemple:"C:\Program Files\Git\usr\bin\xmllint.exe" --format [email protected] > [email protected]
Jeroen Wiert Pluimers
41

Vous pouvez également utiliser tidy , qui peut avoir besoin d'être installé en premier (par exemple sur Ubuntu: sudo apt-get install tidy).

Pour cela, vous émettriez quelque chose comme ceci:

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

Remarque: possède de nombreux indicateurs de lisibilité supplémentaires, mais le comportement de retour à la ligne est un peu ennuyeux à démêler ( http://tidy.sourceforge.net/docs/quickref.html ).

matanster
la source
1
Utile, car je n'ai pas pu obtenir xmllint pour ajouter des sauts de ligne à un fichier xml d'une seule ligne. Merci!
2014
tidyfonctionne bien pour moi aussi. Contrairement à hxnormalizecela, cette opération ferme la <body>balise.
Sridhar Sarnobat
9
BTW, voici quelques options que j'ai trouvé utile: 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
Bon conseil @VictorYarema. Je l'ai combiné avec pygmentize et l'ai ajouté à mon .bashrc: alias prettyxml='tidy --indent yes --indent-spaces 4 --indent-attributes yes --wrap-attributes yes --input-xml yes --output-xml yes | pygmentize -l xml' et puis je peuxcurl url | prettyxml
Net Wolf
13

Vous n'avez pas mentionné de fichier, donc je suppose que vous voulez fournir la chaîne XML comme entrée standard sur la ligne de commande. Dans ce cas, procédez comme suit:

$ echo '<root><foo a="b">lorem</foo><bar value="ipsum" /></root>' | xmllint --format -
David
la source
12

Sans rien installer sur macOS / la plupart des Unix.

Utilisation tidy

cat filename.xml | tidy -xml -iq

Rediriger l'affichage d'un fichier avec cat pour ranger en spécifiant le type de fichier xml et pour mettre en retrait pendant une sortie silencieuse supprimera la sortie d'erreur. JSON fonctionne également avec -json.

jasonleonhard
la source
1
Vous n'avez pas besoin de l' catétape: tidy -xml -iq filename.xml. En outre, vous pouvez même faire en tidy -xml -iq filename.xmlutilisant l' -moption pour modifier le fichier d'origine ...
janniks
10

xmllint prend en charge la mise en forme sur place :

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

Comme Daniel Veillard l'a écrit:

Je pense que cela xmllint -o tst.xml --format tst.xml devrait être sûr car l'analyseur chargera complètement l'entrée dans un arbre avant d'ouvrir la sortie pour la sérialiser.

Le niveau de retrait est contrôlé par XMLLINT_INDENTla variable d'environnement qui est par défaut de 2 espaces. Exemple de modification du retrait à 4 espaces:

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

Il se peut que vous manquiez d' --recoveroption lorsque vos documents XML sont cassés. Ou essayez un analyseur HTML faible avec une sortie XML stricte:

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

--nsclean, --nonet, --nocdata, --noblanksEtc peut être utile. Lisez la page de manuel.

apt-get install libxml2-utils
apt-cyg install libxml2
brew install libxml2
gavenkoa
la source
2

Cela m'a pris une éternité pour trouver quelque chose qui fonctionne sur mon mac. Voici ce qui a fonctionné pour moi:

brew install xmlformat
cat unformatted.html | xmlformat
Sridhar Sarnobat
la source
1
Ma réponse ci-dessus fonctionne sur un mac
jasonleonhard
1

Je voudrais ajouter une solution Bash pure, car ce n'est pas «si» difficile de le faire à la main, et parfois vous ne voudrez pas installer un outil supplémentaire pour faire le travail.

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

Collez-le dans un fichier de script et canalisez dans le xml. Cela suppose que le xml est sur une seule ligne et qu'il n'y a aucun espace supplémentaire nulle part. On pourrait facilement ajouter quelques extra \s*aux regex pour corriger cela.

leondepeon
la source