Pretty-print XML on the command line with xmllint (2026)

xmllint (part of libxml2, already on most systems) reformats ugly, minified, or single-line XML into something readable. Pipe an API response straight through it, control the indent width, and validate while you're at it.

TL;DR — xmllint --format file.xml pretty-prints XML. Read from a pipe with -, control indentation with XMLLINT_INDENT, and write back with --output. It ships with libxml2, so it’s almost certainly already installed.

This one first went up here in 2012 — I was poking at a Visa 3-D Secure test directory that answered in single-line XML and needed it readable. The tool hasn’t changed; the flags below are the current, double-dash forms.

The problem

API responses come back as one unreadable line. Here’s the kind of thing a payment directory server hands you:

<?xml version="1.0" encoding="UTF-8"?><ThreeDSecure><Message><VERes><version>1.0.2</version><CH><enrolled>Y</enrolled></CH><url>https://acs.example/PIT/ACS</url></VERes></Message></ThreeDSecure>

Format a file

xmllint --format response.xml

That writes the indented version to stdout. To overwrite (or write a new file):

xmllint --format response.xml --output response.pretty.xml

Format a stream (the useful one)

Pass - as the filename to read stdin, so you can pretty-print straight from curl:

curl -s https://api.example/thing | xmllint --format -
<?xml version="1.0" encoding="UTF-8"?>
<ThreeDSecure>
  <Message>
    <VERes>
      <version>1.0.2</version>
      <CH>
        <enrolled>Y</enrolled>
      </CH>
      <url>https://acs.example/PIT/ACS</url>
    </VERes>
  </Message>
</ThreeDSecure>

Control the indentation

By default xmllint indents with two spaces. Override it with the XMLLINT_INDENT environment variable — set it to spaces or a tab:

XMLLINT_INDENT="    " xmllint --format response.xml     # 4 spaces
XMLLINT_INDENT=$'\t'   xmllint --format response.xml     # tabs

Recover broken XML

If the document is slightly malformed (a stray entity, a missing close tag from a truncated download), add --recover to format what it can instead of bailing:

xmllint --recover --format broken.xml

While you’re there: validate

xmllint isn’t just a formatter. Check well-formedness (exit code 0 = OK), or validate against a schema:

xmllint --noout response.xml                       # well-formed?
xmllint --noout --schema schema.xsd response.xml   # valid against XSD?
xmllint --noout --dtdvalid doc.dtd response.xml    # valid against DTD?

Install (if it’s somehow missing)

sudo apt install libxml2-utils     # Debian / Ubuntu
sudo dnf install libxml2           # Fedora
brew install libxml2               # macOS (then use the keg's bin)

FAQ

How do I minify XML instead of pretty-printing it?

Use --noblanks: xmllint --noblanks file.xml strips the insignificant whitespace.

Can I extract a single value instead of formatting the whole thing?

Yes — xmllint --xpath '//url/text()' response.xml pulls out a node with XPath. Handy for scripting against XML APIs.

What about JSON?

xmllint is XML-only. For JSON, jq . is the equivalent (“jq for XML” is roughly xmllint --format).

Summary

  • xmllint --format file.xml — pretty-print a file.
  • … | xmllint --format - — pretty-print a stream.
  • XMLLINT_INDENT — set the indent.
  • --recover, --noout --schema, --xpath — fix, validate, and query.