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
--xpathcannot bind prefixes directly. You can work around this withlocal-name():"//*[local-name()='element']" - The
--xpathflag was added in libxml2 2.7.7. Older systems may only have the interactive--shellmode. - When the XPath matches multiple nodes the output is concatenated with no separator. Wrap in a
for-eachor post-process withsedif 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 < and &, 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
| Flag | What it does |
|---|---|
| --noblanks | Remove insignificant whitespace (minify). Combine with --format to re-indent from scratch. |
| --c14n | Canonicalize (C14N) the XML — useful for comparing documents that differ only in attribute order or namespace declarations. |
| --encode UTF-8 | Re-encode the output (e.g. convert ISO-8859-1 to UTF-8). |
| --recover | Try to produce output even from broken XML. Useful for salvaging data from malformed exports. |
| --html | Parse as HTML instead of XML. Lets you use xmllint to pretty-print or query HTML pages. |
| --dropdtd | Remove 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()ormatches(), you need Saxon orxmlstarlet. - 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