XMLMath is a command-line tool for performing arithmetic operations directly on XML files. It allows batch modifications of attributes and values using a flexible set of filters and modifiers, allowing automated XML numeric data transformations.
- Run arbitrary (awk-compatible) mathematic operations on attribute or element values
- Target elements or attributes via tag names, or additional regex
- Clamp and/or round results
- Modify files in-place or output changes to a file or stdout
- Optionally diff changes to stdout or a file
- Small dependency footprint
- Fast. It really isn't.
- Suitable for non-math operations. You'll be better off with regex-based find-replace in any text editor.
XMLMath relies on awk, sed, diff and xmllint. With the exception of xmllint, these are already included in many distributions. On Debian-/Ubuntu-based distros, xmllint can be installed with:
sudo apt install libxml2-utils
Download the built version of XMLMath from the latest release. You could put it somewhere that's in your PATH to make it available globally.
curl -L -o xmlmath https://github.com/crowbait/xmlmath/releases/latest/download/xmlmath
chmod +x xmlmath
You can also run it directly; this ensures you're always working with the latest release and doesn't put the script file on your drive.
curl -sL https://github.com/crowbait/xmlmath/releases/latest/download/xmlmath | bash -s -- "parameter1" "parameter2" "..."
You'll need to supply all parameters in quotes.
Syntax:
./xmlmath <attr|value> <operation> <modifier> [options] <...targets>
This multiplies the value (between tags) of all XML elements named "someval" or "otherval" in input.xml
by 2, writing the results back to the same file:
./xmlmath value multiply 2 --file input.xml --inplace someval otherval
This multiplies the value all attributes in the file in.xml
with names matching the regex .*someattr
AND which are attributes of a tag "tagname" by 1.1, rounding the results to the neares integer and ensuring they are always greater than or equal to 2:
./xmlmath attr multiply 1.1 --int --min 2 --file in.xml --regex -w tagname '.*someattr'
You can run arbitrary operations on the found values, providing they can be expressed in awk-compatible syntax - in this case, all attribute values (see regex) will be replaced with their root added to their squared value.
The token x
will be substituted for the found value:
./xmlmath attr expression 'sqrt(x)+x*x' --file in.xml -r '.*'
You can get more examples from the tests: test.sh
list commands.
The index names of those commands correspond to files in test/
, which contain the transformed output when running these commands on test/_expectation-template.xml
.
Operation | Short | Description |
---|---|---|
add | a | Add the modifier to the found values |
subtract | s | Subtract the modifier from the found values |
multiply | m | Multiply the found values with the modifier |
divide | d | Divide the found values by the modifier |
set | Set the found values to the modifier | |
expression | e | Treat the modifier as an expression; must be awk-compatible syntax and should be (single-)quoted |
Option | Short | Arg | Description |
---|---|---|---|
--file | -f | Path | Specifies a file to be used as input. If not given, input is taken from stdin. |
requires -f --inplace |
-p | Writes the output back to the same file used as input. | |
--out | -o | Path | Writes the output to Path. Cannot be used with --inplace. |
--int | -i | Rounds results to the nearest integer. | |
--min | Number | Clamps all operation results to a minimum value of Number. | |
--max | Number | Clamps all operation results to a maximum value of Number. | |
--regex | -r | Treats all targets (the list of attribute or value identifiers) as regex patterns instead of exact matches. You should quote each entry; see examples. | |
--within | -w | Tag | For attr: only matches attributes within the tag Tag. For val: only matches values that are a child element of Tag - within any depth. |
requires -w --within-additional |
--wa | Regex | Only accepts a match on --within if that line (containing the tag) also satisfies Regex. |
--diff | -d | Displays changes after all operations have finished. | |
requires -d --diff-line-length |
--dl | Number | Limits the length of lines in --diff 's output to Number, truncating as needed. |
requires -d --diff-file |
--df | Path | Writes diff to a file instead of stdout. |
--verbose | -v | Prints detailed information about what's happening. This WILL make your stdout unusable as XML. | |
--progress | Prints progress information. Prints to stderr, so the stdout should not be affected. Cannot be used with -v . |
The main script is modularized and source
s its modules, which are in parts/*
.
This is done for maintainability and is mostly personal preference.
It can be run directly in its development form or can be "compiled" by injecting the external modules into the script.
This is done by build.sh
and writes a complete, self-contained file to build/xmlmath
.
Testing is done using test.sh
.
Within that script, there is an array, mapping a "test-id" to a command to run against the main script.
The input for that test run is the file test/test.xml
, which is intentionally ill-formatted.
There must be a file test/[test-id].xml
, which represents the expected output of the run.
New test-target-XMLs should be created from test/_expectation-template.xml
(same content as test.xml
, but well formatted).