Skip to content
Format XML

XML Tools

xmllint Cheat Sheet

Everything you need to know about xmllint — the command-line XML processor that ships with libxml2. Format, validate, and query XML files without leaving your terminal.

What is xmllint?

xmllint is a command-line utility bundled with libxml2, the XML parsing library used by countless projects from Apache to PHP. It can parse, validate, reformat, and query XML documents — all from a single binary.

On macOS it is pre-installed. On most Linux distributions it comes with the libxml2-utils package. On Windows you can get it through MSYS2, Chocolatey, or WSL.

Installing xmllint

Check whether you already have it:

xmllint --version

If the command is not found, install it for your OS:

# Debian / Ubuntu
sudo apt-get install libxml2-utils

# Fedora / RHEL
sudo dnf install libxml2

# macOS (already installed — update via Homebrew if needed)
brew install libxml2

# Windows (Chocolatey)
choco install xsltproc   # includes xmllint

Pretty-print (format) XML

The most common use of xmllint is reformatting minified or poorly indented XML into a human-readable tree. The --format flag does exactly that:

xmllint --format input.xml

Output goes to stdout. To save it back to a file:

xmllint --format input.xml > formatted.xml

By default xmllint indents with two spaces. You can control the indent string with the XMLLINT_INDENT environment variable:

# Indent with 4 spaces
XMLLINT_INDENT="    " xmllint --format input.xml

# Indent with tabs
XMLLINT_INDENT=$'\t' xmllint --format input.xml

If you need to format XML from a pipeline (e.g. a curl response), read from stdin with -:

curl -s https://example.com/feed.xml | xmllint --format -

Validate XML

xmllint checks well-formedness by default — any parse error is reported with the line and column number. You can also validate against a schema:

Well-formedness check

xmllint --noout input.xml

The --noout flag suppresses output so you only see errors. Exit code 0 means the file is well-formed.

DTD validation

# Validate against the DTD declared in the document
xmllint --valid --noout input.xml

# Validate against an external DTD
xmllint --dtdvalid schema.dtd --noout input.xml

XSD (XML Schema) validation

xmllint --schema schema.xsd --noout input.xml

xmllint prints each validation error with context. For large files with many errors, pipe the output through head to avoid flooding your terminal.

Query XML with XPath

The --xpath flag evaluates an XPath expression and prints the result:

# Extract all <title> elements
xmllint --xpath "//title" input.xml

# Get the text content of the first <name>
xmllint --xpath "string(//name[1])" input.xml

# Count how many <item> elements exist
xmllint --xpath "count(//item)" input.xml

A few things to watch out for:

  • If your XML uses namespaces, xmllint’s --xpath cannot bind prefixes directly. You can work around this with local-name(): "//*[local-name()='element']"
  • The --xpath flag was added in libxml2 2.7.7. Older systems may only have the interactive --shell mode.
  • When the XPath matches multiple nodes the output is concatenated with no separator. Wrap in a for-each or post-process with sed if you need newlines between results.

Common xmllint errors and fixes

Here are the error messages you will run into most often and what they mean:

parser error : StartTag: invalid element name

Usually means an unescaped < or & in text content. Replace them with &lt; and &amp;, or wrap the text in a CDATA section.

parser error : Opening and ending tag mismatch

A closing tag does not match its opening tag. Check for typos and case mismatches (XML tags are case-sensitive).

parser error : Extra content at the end of the document

XML allows only one root element. If you have multiple top-level elements, wrap them in a single parent, for example <root>...</root>.

failed to load external entity

xmllint cannot find the file you passed. Double-check the path. If you meant to read from stdin, use - as the filename.

Other useful flags

FlagWhat it does
--noblanksRemove insignificant whitespace (minify). Combine with --format to re-indent from scratch.
--c14nCanonicalize (C14N) the XML — useful for comparing documents that differ only in attribute order or namespace declarations.
--encode UTF-8Re-encode the output (e.g. convert ISO-8859-1 to UTF-8).
--recoverTry to produce output even from broken XML. Useful for salvaging data from malformed exports.
--htmlParse as HTML instead of XML. Lets you use xmllint to pretty-print or query HTML pages.
--dropdtdRemove the DTD declaration from output. Handy when a DTD reference points to a server that no longer exists.

Scripting recipes

Format all XML files in a directory

for f in *.xml; do
  xmllint --format "$f" > "$f.tmp" && mv "$f.tmp" "$f"
done

Validate every XML file in a CI pipeline

find . -name "*.xml" -exec xmllint --noout {} +

The exit code is non-zero if any file has errors, so your CI job fails automatically.

Compare two XML files ignoring whitespace

diff <(xmllint --c14n a.xml) <(xmllint --c14n b.xml)

Canonicalizing both files first removes differences in indentation, attribute order, and namespace prefixes so you only see real content changes. For a more detailed comparison, see our guide on how to compare XML files.

When xmllint falls short

xmllint is powerful for a bundled utility, but it has real limitations:

  • No RELAX NG compact syntax. It supports RELAX NG in XML form (via --relaxng) but not the compact (.rnc) format.
  • Namespace-aware XPath is awkward. Without proper prefix binding you end up writing verbose local-name() expressions.
  • No XPath 2.0+. If you need functions like tokenize() or matches(), you need Saxon or xmlstarlet.
  • No syntax highlighting. Terminal output is plain text — for a visual, color-coded view you need a GUI or an online XML formatter.

Skip the terminal — format XML in your browser

No installation, no flags to remember. Paste your XML, pick an indent style, and get formatted output with syntax highlighting and instant validation — all client-side, no upload required.

Open Format XML