¿Cómo imprimir bastante XML desde la línea de comando?

528

Relacionado: ¿Cómo puedo imprimir JSON en script de shell (unix)?

¿Existe un script de shell (unix) para formatear XML en forma legible para humanos?

Básicamente, quiero que transforme lo siguiente:

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

... en algo como esto:

<root>
    <foo a="b">lorem</foo>
    <bar value="ipsum" />
</root>
svidgen
fuente
1
Para tener xmllintdisponible en los sistemas Debian, debe instalar el paquete libxml2-utils( libxml2no proporciona esta herramienta, al menos no en Debian 5.0 "Lenny" y 6.0 "Squeeze").
Twonkeys

Respuestas:

909

libxml2-utils

Esta utilidad viene con libxml2-utils:

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

Perl's XML::Twig

Este comando viene con XML :: Twig módulo, a veces xml-twig-toolspaquete:

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

xmlstarlet

Este comando viene con xmlstarlet:

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

tidy

Comprueba el tidypaquete:

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

Pitón

Python xml.dom.minidompuede formatear XML (tanto python2 como 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

Necesitas saxon-lint:

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

saxon-HE

Necesitas 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
fuente
Buena respuesta rápida. Parece que la primera opción será más ubicua en las instalaciones modernas * nix. Un punto menor; pero ¿se puede llamar sin trabajar a través de un archivo intermedio? Es decir, echo '<xml .. />' | xmllint --some-read-from-stdn-option?
svidgen
El paquete está libxml2-utilsen mi hermoso ubuntu.
franzlorenzon
1
Tenga en cuenta que el "cat data.xml | xmllint --format - | tee data.xml" no funciona. En mi sistema a veces funcionaba para archivos pequeños, pero siempre truncaba archivos grandes. Si realmente desea hacer algo en su lugar, lea backreference.org/2011/01/29/in-place-editing-of-files
user1346466
1
Para resolver UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 805: ordinal not in range(128)en la versión de Python que desea definir 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
Tenga en cuenta que tidy también puede formatear xml sin elemento raíz . Esto es útil para formatear a través de una tubería, secciones xml (por ejemplo, extraídas de registros). echo '<x></x><y></y>' | tidy -xml -iq
Marinos un
157

xmllint --format yourxmlfile.xml

xmllint es una herramienta XML de línea de comando y se incluye en libxml2( http://xmlsoft.org/ ).

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

Nota: Si no lo ha libxml2instalado, puede instalarlo haciendo lo siguiente:

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

Mac OS

Para instalar esto en MacOS con Homebrew solo haz: brew install libxml2

Git

También disponible en Git si quieres el código: git clone git://git.gnome.org/libxml2

crmpicco
fuente
44
La respuesta de sputnick contiene esta información, pero la respuesta de crmpicco es la respuesta más útil aquí a la pregunta general sobre cómo imprimir bastante XML.
Seth Difley
2
podemos escribir esa salida xml formateada en algún otro archivo xml y usar eso .. ej. xmllint --format yourxmlfile.xml >> new-
file.xml
2
En Ubuntu 16.04 puede usar lo siguiente:sudo apt-get install libxml2-utils
Melle
Esto también funciona en Windows; gitpara la descarga de Windows, incluso instala una versión reciente de xmllint. Ejemplo:"C:\Program Files\Git\usr\bin\xmllint.exe" --format [email protected] > [email protected]
Jeroen Wiert Pluimers
41

También puede usar tidy , que puede necesitar instalarse primero (por ejemplo, en Ubuntu: sudo apt-get install tidy).

Para esto, emitirías algo como lo siguiente:

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

Nota: tiene muchos indicadores de legibilidad adicionales, pero el comportamiento de ajuste de palabras es un poco molesto de desenredar ( http://tidy.sourceforge.net/docs/quickref.html ).

Matanster
fuente
1
Útil, porque no pude hacer que xmllint agregue saltos de línea a un archivo xml de una sola línea. ¡Gracias!
xlttj
tidyA mí también me funciona bien. A diferencia de hxnormalizeesto, en realidad cierra la <body>etiqueta.
Sridhar Sarnobat
99
Por cierto, aquí hay algunas opciones que he encontrado útiles: 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
Gran consejo @VictorYarema. Lo combiné con pygmentize y lo agregué a mi .bashrc: alias prettyxml='tidy --indent yes --indent-spaces 4 --indent-attributes yes --wrap-attributes yes --input-xml yes --output-xml yes | pygmentize -l xml' y luego puedocurl url | prettyxml
Net Wolf el
13

No mencionó un archivo, por lo que supongo que desea proporcionar la cadena XML como entrada estándar en la línea de comando. En ese caso, haga lo siguiente:

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

Sin instalar nada en macOS / most Unix.

Utilizar tidy

cat filename.xml | tidy -xml -iq

Redirigir la visualización de un archivo con cat para ordenar especificando el tipo de archivo de xml y sangrar mientras la salida silenciosa suprimirá la salida de error. JSON también funciona con -json.

jasonleonhard
fuente
1
No es necesario el catpaso: tidy -xml -iq filename.xml. Además, incluso puedes tidy -xml -iq filename.xmlusar la -mopción para modificar el archivo original ...
Janniks
10

Formato de soporte de xmllint en el lugar :

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

Como Daniel Veillard ha escrito:

Creo que xmllint -o tst.xml --format tst.xml debería ser seguro ya que el analizador cargará completamente la entrada en un árbol antes de abrir la salida para serializarla.

El nivel de sangría está controlado por XMLLINT_INDENTla variable de entorno, que es por defecto 2 espacios. Ejemplo de cómo cambiar sangría a 4 espacios:

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

Es posible que le falte la --recoveropción cuando los documentos XML están rotos. O pruebe un analizador HTML débil con una salida XML estricta:

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

--nsclean, --nonet, --nocdata, --noblanksEtc pueden ser útiles. Leer la página del manual.

apt-get install libxml2-utils
apt-cyg install libxml2
brew install libxml2
gavenkoa
fuente
2

Esto me llevó una eternidad a encontrar algo que funcione en mi Mac. Esto es lo que funcionó para mí:

brew install xmlformat
cat unformatted.html | xmlformat
Sridhar Sarnobat
fuente
1
Mi respuesta anterior funciona en una Mac
jasonleonhard
1

Me gustaría agregar una solución Bash pura, ya que no es 'tan' difícil hacerlo solo a mano, y a veces no querrá instalar una herramienta adicional para hacer el trabajo.

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

Pégalo en un archivo de secuencia de comandos y canaliza el xml Esto supone que el xml está todo en una línea y que no hay espacios adicionales en ningún lado. Uno podría agregar fácilmente algo extra \s*a las expresiones regulares para arreglar eso.

leondepeon
fuente