Skip to content

UTL-X on the Command Line ​

UTL-X was designed to feel at home in a terminal. If you've used jq for JSON processing, UTL-X will feel familiar — but it works with XML, CSV, YAML, and OData too. This chapter covers the three CLI modes, shell scripting patterns, and when to use each.

Three Ways to Use the CLI ​

ModeSyntaxBest for
Transformutlx transform script.utlx input.xmlComplex transformations with a .utlx file
Expression (-e)`echo datautlx -e '.name'`
Identity (flip)`cat data.xmlutlx`

Expression Mode: The jq Experience ​

Expression mode (-e) is the most powerful way to use UTL-X interactively. Instead of writing a .utlx file, you pass the transformation as a command-line argument:

bash
echo '{"name": "Alice", "age": 30}' | utlx -e '.name'
# "Alice"

The dot (.) is shorthand for $input. So .name means $input.name, and . alone means the entire input — the identity expression. This mirrors jq's syntax deliberately: utlx -e '.' is the UTL-X equivalent of jq '.'.

Raw Output (-r) ​

By default, string output includes quotes. Use -r (raw) to strip them — essential for piping to other commands:

bash
# With quotes (default)
echo '{"name": "Alice"}' | utlx -e '.name'
# "Alice"

# Raw (no quotes) — pipe-friendly
echo '{"name": "Alice"}' | utlx -e '.name' -r
# Alice

# Use in shell variables
NAME=$(echo '{"name": "Alice"}' | utlx -e '.name' -r)
echo "Hello, $NAME"
# Hello, Alice

Expressions with Any Format ​

Unlike jq, expression mode works with XML, CSV, and YAML too:

bash
# Extract from XML
echo '<User><Name>Alice</Name></User>' | utlx --from xml -e '.User.Name' -r
# Alice

# Extract from XML attribute
echo '<Product id="P-001"/>' | utlx --from xml -e '.Product.@id' -r
# P-001

# Extract from YAML
echo 'server: prod-01\nport: 8080' | utlx --from yaml -e '.port' -r
# 8080

# Extract from CSV (first row)
echo 'name,age\nAlice,30\nBob,25' | utlx --from csv -e '.[0].name' -r
# Alice

Complex Expressions ​

Expressions can be as simple or complex as needed:

bash
# Object construction
echo '{"first": "Alice", "last": "Johnson"}' | utlx -e '{fullName: concat(.first, " ", .last)}'
# {"fullName": "Alice Johnson"}

# Array operations
echo '[3, 1, 4, 1, 5, 9]' | utlx -e 'sort($input)'
# [1, 1, 3, 4, 5, 9]

# Filter and map
echo '{"users": [{"name": "Alice", "active": true}, {"name": "Bob", "active": false}]}' \
  | utlx -e 'map(filter(.users, (u) -> u.active), (u) -> u.name)'
# ["Alice"]

# Aggregation
echo '[{"amount": 10}, {"amount": 20}, {"amount": 30}]' | utlx -e 'sum(map($input, (x) -> x.amount))'
# 60

Identity Mode: The Flip ​

The simplest way to use UTL-X — pipe data in, get a different format out. No script, no expression, no arguments:

bash
# XML → JSON (automatic)
cat order.xml | utlx
# {"Order": {"Customer": "Alice", "Total": 299.99}}

# JSON → XML (automatic)
echo '{"name": "Alice"}' | utlx
# <?xml version="1.0" encoding="UTF-8"?><name>Alice</name>

UTL-X auto-detects the input format and "flips" to the most useful output format:

Input detected asOutput format (default flip)
XMLJSON
JSONXML
CSVJSON
YAMLJSON

Override the output format with --to:

bash
# XML → YAML (instead of default JSON)
cat order.xml | utlx --to yaml

# JSON → CSV
echo '[{"name":"Alice","age":30},{"name":"Bob","age":25}]' | utlx --to csv

# CSV → YAML
cat report.csv | utlx --to yaml

Override the input format with --from (when auto-detection fails):

bash
# Force input as CSV (e.g., when piped from another command)
some-command | utlx --from csv --to json

Shell Scripting Patterns ​

Batch File Conversion ​

Convert all XML files in a directory to JSON:

bash
for f in *.xml; do
  utlx --from xml --to json < "$f" > "${f%.xml}.json"
done

Pipeline Integration ​

UTL-X is pipe-friendly — combine it with standard Unix tools:

bash
# Extract customer names from XML orders, sort, deduplicate
cat orders.xml | utlx --from xml -e 'map(.Orders.Order, (o) -> o.Customer)' \
  | utlx -e 'sort($input)' \
  | utlx -e 'unique($input)'

# Count items in a JSON array
curl -s https://api.example.com/users | utlx -e 'count($input)' -r

# Filter CSV rows and convert to JSON
cat report.csv | utlx --from csv -e 'filter($input, (row) -> toNumber(row.amount) > 100)' --to json

Using UTL-X in CI/CD ​

Validate a configuration file in a GitHub Actions workflow:

yaml
- name: Validate config
  run: |
    cat config.yaml | utlx --from yaml -e 'if (.version != null && .database != null) "valid" else "invalid"' -r

Transform API responses in deployment scripts:

bash
# Extract image tag from Kubernetes deployment
kubectl get deployment myapp -o json | utlx -e '.spec.template.spec.containers[0].image' -r

Combining with jq ​

UTL-X and jq can coexist. Use jq for JSON-only tasks where its syntax is more concise, and UTL-X when you need XML, CSV, or YAML:

bash
# jq for simple JSON field extraction (shorter syntax)
echo '{"name": "Alice"}' | jq -r '.name'

# UTL-X when input is XML (jq can't do this)
echo '<User><Name>Alice</Name></User>' | utlx --from xml -e '.User.Name' -r

# Combine: UTL-X converts XML to JSON, jq processes further
cat complex.xml | utlx --from xml --to json | jq '.Orders[] | select(.total > 100)'

The UTL-X jq Cheat Sheet ​

For developers coming from jq, here's how common patterns translate:

TaskjqUTL-X
Extract fieldjq '.name'utlx -e '.name'
Raw outputjq -r '.name'utlx -e '.name' -r
Array length`jq '.length'`
Map array`jq '[.[].name]'`
Filter array`jq '[.[]select(.active)]'`
Sortjq 'sort_by(.name)'utlx -e 'sortBy(., (x) -> x.name)'
Keysjq 'keys'utlx -e 'keys(.)'
Sum`jq '[.[].price]add'`
Compactjq -c '.'utlx -e '.' --no-pretty

The key difference: every UTL-X command above also works with --from xml, --from csv, or --from yaml. jq is JSON-only.

When to Use Which Mode ​

SituationUse
Quick field extractionutlx -e '.field' -r
Format conversion (no logic)`cat data.xml
Format conversion (explicit target)`cat data.xml
Complex transformation with reuseutlx transform script.utlx input.xml
Shell pipeline`some-cmd
CI/CD validationutlx -e 'if (...) "ok" else "fail"' -r
Production serviceNot CLI — use utlxe (see Chapter 6)

The CLI (utlx) is for humans and scripts. The engine (utlxe) is for production services. They share the same language — a transformation that works in -e mode works identically in utlxe.

Performance Notes ​

The native binary (GraalVM) is critical for CLI usage:

  • Startup: < 10ms (native) vs  250ms (JVM JAR)

  • For single invocations: startup time dominates — native is 25x faster end-to-end

  • For shell loops: for f in *.xml; do utlx ...; done — native processes  100 files/second, JVM  4 files/second (startup penalty per invocation)

  • For pipes: cat data | utlx -e '...' — native feels instant, JVM has noticeable delay

If you're using UTL-X in scripts or CI/CD, always use the native binary. The JVM JAR is for development only (when you need debugging or hot-reload).

Released under AGPL-3.0.