CLI Commands¶
All commands follow the pattern exsh <command> [options]. Paths to remote documents and collections are written as <nick>:<path> (e.g. mydata:reports/2025/summary.xml), where <nick> is the short name you gave to a registered collection.
ls¶
List subcollections and documents at a collection path.
Output has one entry per line with columns separated by two spaces. Subcollections are shown with a trailing /. Empty fields (e.g. size for a collection) are omitted.
Columns for collections: name/, permissions, owner, created
Columns for resources: name, permissions, owner, size (bytes), mime-type, last-modified
Options¶
| Flag | Description |
|---|---|
--sort name |
Sort entries alphabetically by name (default) |
--sort time |
Sort by modification time (oldest first) |
--reverse / -r |
Reverse the sort order |
--names-only |
Print only names, one per line — useful for piping |
Examples¶
# List the root of a registered collection
exsh ls mydata
# List a subdirectory
exsh ls mydata:reports/2025
# Sort by modification time, newest first
exsh ls mydata:reports/2025 --sort time --reverse
# Pipe only filenames into another command
exsh ls mydata:reports/2025 --names-only | xargs -I{} exsh cat mydata:reports/2025/{}
# Pipe into grep
exsh ls mydata:reports/2025 | grep ".xml"
cat¶
Print the content of a document to stdout.
By default cat refuses to print binary content and shows an error. Use --raw to write raw bytes to stdout instead.
Options¶
| Flag | Description |
|---|---|
--raw |
Write raw bytes to stdout regardless of MIME type |
Examples¶
# Print an XML document
exsh cat mydata:reports/2025/summary.xml
# Pipe into xmllint
exsh cat mydata:reports/2025/summary.xml | xmllint --format -
# Download a binary to a file
exsh cat --raw mydata:images/logo.png > logo.png
put¶
Upload a local file (or stdin) to a collection path.
If the target document already exists it is silently overwritten. The MIME type is guessed from the file extension when not specified; it falls back to application/xml.
Before uploading, exsh checks that the content is well-formed XML whenever the resolved MIME type is an XML type (application/xml, text/xml, or any type ending in +xml). If the check fails, the upload is refused and the parse error is printed. Non-XML MIME types (e.g. image/png, application/json) are uploaded without any validation.
Options¶
| Flag | Description |
|---|---|
-f / --file FILE |
Local file to upload. When omitted, stdin is read. |
--mime MIME |
Explicit MIME type. Overrides guessed value. |
--allow-malformed |
Skip XML well-formedness check and upload regardless. |
Examples¶
# Upload a file
exsh put mydata:reports/2025/report.xml -f report.xml
# Upload from stdin
cat report.xml | exsh put mydata:reports/2025/report.xml
# Override MIME type
exsh put mydata:data/config.json -f config.json --mime application/json
# Upload binary
exsh put mydata:images/logo.png -f logo.png --mime image/png
# Upload a work-in-progress XML file that is not yet well-formed
exsh put mydata:drafts/wip.xml -f wip.xml --allow-malformed
cp¶
Copy a document between local paths and remote eXist collections. All three directions are supported.
If the target is an existing local directory, the source filename is appended automatically. If the target path ends with /, the source filename is also appended.
Copy directions¶
| Source | Target | Effect |
|---|---|---|
nick:path |
local path | Download remote document to local file |
| local path | nick:path |
Upload local file to remote collection |
nick:path |
nick:path |
Copy between two remote locations (may be different servers) |
Examples¶
# Remote to local (into a specific file)
exsh cp mydata:reports/2025/summary.xml ./summary.xml
# Remote to local (into a directory — filename preserved)
exsh cp mydata:reports/2025/summary.xml ./downloads/
# Local to remote
exsh cp ./report.xml mydata:reports/2025/report.xml
# Remote to remote (same or different servers)
exsh cp mydata:reports/2025/summary.xml archive:reports/2025/summary.xml
edit¶
Download a document, open it in your editor, and automatically re-upload if the content changed.
The editor is resolved from the $VISUAL environment variable, then $EDITOR, falling back to vi. If you use VS Code, set EDITOR="code --wait".
If the file is not modified, exsh prints No changes. and exits without uploading.
For XML documents, the content is checked for well-formedness before uploading. If the check fails, exsh prints the parse error, prompts you to press Enter, and re-opens the editor so you can fix the problem in place. Quitting the editor without making further changes aborts the upload.
Options¶
| Flag | Description |
|---|---|
--allow-malformed |
Skip XML well-formedness check and upload regardless. |
Examples¶
# Edit with whatever $EDITOR is set to
exsh edit mydata:reports/2025/summary.xml
# Temporarily override the editor
EDITOR=nano exsh edit mydata:config.xml
# Upload even if the resulting XML is not well-formed
exsh edit mydata:drafts/wip.xml --allow-malformed
rm¶
Delete one or more documents from an eXist collection.
Multiple paths can be supplied in a single invocation. Deletion is permanent — there is no undo.
Examples¶
# Delete a single document
exsh rm mydata:reports/2025/old.xml
# Delete multiple documents at once
exsh rm mydata:reports/2025/a.xml mydata:reports/2025/b.xml
mkdir¶
Create a subcollection inside a registered collection.
Parent collections must already exist. Use multiple mkdir calls to create nested paths.
Examples¶
# Create a subcollection
exsh mkdir mydata:reports/2026
# Create a nested path (parents must exist first)
exsh mkdir mydata:reports
exsh mkdir mydata:reports/2026
exsh mkdir mydata:reports/2026/q1
sync¶
Synchronise a local directory tree and a remote collection, transferring only changed files.
See the dedicated sync page for full details.
exsh sync <source> <dest> [--force] [--allow-malformed] [--fail-fast] [--dry-run] [--delete] [--verbose]
Direction is inferred from argument order: local-first means push, remote-first means pull.
Options¶
| Flag | Description |
|---|---|
--force / -f |
Transfer all files, bypassing conflict detection |
--allow-malformed |
Upload XML files even if they are not well-formed (push only) |
--fail-fast |
Stop on the first conflict or XML validation failure; manifest is saved so the run can resume (push only) |
--dry-run / -n |
Show what would happen without transferring anything |
--delete |
Remove files on the destination that no longer exist on the source |
--verbose / -v |
Also print unchanged (skipped) files |
Quick examples¶
# Push local folder to server
exsh sync ./reports mydata:reports
# Pull remote collection to local folder
exsh sync mydata:reports ./reports
# Preview a push without doing anything
exsh sync --dry-run ./reports mydata:reports
# Push and delete server-side extras
exsh sync --delete ./reports mydata:reports
# Push but stop immediately if any XML file is malformed
exsh sync --fail-fast ./reports mydata:reports
exec¶
Execute an XQuery script on an eXist-db server and print the result to stdout.
The query is read from --file or from stdin. Before sending, exsh optionally preprocesses the source and validates it locally:
Preprocessing (enabled by default, skip with --no-fix):
- Adds
xquery version "3.1";if no version declaration is present. - Adds the
functxmodule import iffunctx:functions are referenced but not declared.
Local validation (enabled when a supported validator is installed, skip with --no-validate):
The first installed validator found on PATH is used automatically. Use --validator to choose a specific one. Supported validators:
| Name | Tool |
|---|---|
basex |
BaseX |
saxon |
Saxon (requires a saxon wrapper script on PATH) |
Run exsh exec --list-validators to see which validators are available on the current machine.
Options¶
| Flag | Description |
|---|---|
-f / --file FILE |
XQuery file to execute. When omitted, stdin is read. |
--no-fix |
Skip preprocessing (version declaration, namespace imports). |
--no-validate |
Skip local validation even if a validator is installed. |
--validator NAME |
Use a specific local validator by name. |
--list-validators |
List known validators and their install status, then exit. |
Examples¶
# Execute a query file against the root of a collection
exsh exec mydata:/ -f query.xq
# Execute from stdin
echo 'count(collection("/db/mydata"))' | exsh exec mydata:/
# Pipe the result into xmllint for pretty-printing
exsh exec mydata:/ -f query.xq | xmllint --format -
# Execute in the context of a subcollection
exsh exec mydata:reports/2025 -f summary.xq
# Skip preprocessing and validation (e.g. for already-complete scripts)
exsh exec mydata:/ --no-fix --no-validate -f query.xq
# Force a specific validator
exsh exec mydata:/ --validator basex -f query.xq
# Check which validators are available locally
exsh exec --list-validators