Skip to main content

Command-line tool to interact with eXist-db via REST

Project description

exsh — eXist-db Shell

Tests Coverage e2e Ruff ty docs

A command-line tool to interact with an eXist-db server via its REST API. Designed for shell scripting and pipe-friendly workflows.

Requirements

  • Python 3.11+
  • uv

Installation

Install system-wide with uv tool:

uv tool install exist-shell

Or with pipx:

pipx install exist-shell

This places exsh on your PATH. Verify with:

exsh --version

To uninstall:

uv tool uninstall exist-shell

Run without installing

uvx --from exist-shell exsh --version

uvx fetches the package into a temporary, cached environment and runs it — handy for one-off use or trying out a new release.

Install from git

To track an unreleased commit instead of a PyPI release:

uv tool install git+https://github.com/ambs/exist-shell

Configuration

Add a server

exsh server add localhost --port 8080 --user admin

A nickname is derived from the hostname by default (e.g. localhost). Override with --nick.

Add an existing collection

exsh collection add mydata@localhost

This registers the /db/mydata collection on the localhost server under the nick mydata.

Create and register a new collection

exsh collection new mydata@localhost

Creates /db/mydata on the server and registers it in one step. If the collection already exists, it prints a message and exits without modifying the config. Use --nick to register it under a different name:

exsh collection new mydata@localhost --nick md

List configured servers and collections:

exsh server ls
exsh collection ls

Configuration is stored at ~/.config/exsh/config.toml.

Commands

Command Description
exsh ls <nick>[:<path>] List subcollections and documents at a path
exsh cat <nick>:<path> Print a document to stdout
exsh put <file> <nick>:<path> Upload a local file to a collection
exsh cp <src> <dst> Copy a document (local ↔ remote or remote ↔ remote)
exsh edit <nick>:<path> Open a document in $EDITOR, re-upload if changed
exsh rm <nick>:<path>... Delete one or more documents
exsh mkdir <nick>:<path> Create a collection
exsh sync <local> <nick>[:<path>] Push a local folder to a remote collection
exsh sync <nick>[:<path>] <local> Pull a remote collection to a local folder
exsh exec <nick>[:<path>] Execute an XQuery script on a server
exsh server add <host> Register a server
exsh server ls List registered servers
exsh server rm <nick> Remove a server (and its collections)
exsh server rename <old> <new> Rename a server nick (updates collection references)
exsh collection add <name>[@<server>] Register an existing collection
exsh collection new <name>[@<server>] Create a collection on the server and register it
exsh collection ls List registered collections
exsh collection rm <nick> Remove a collection from the config
exsh user ls [@server] List user accounts and their groups
exsh user add <user[@server]> Create a user account (prompts for password)
exsh user rm <user[@server]> Remove a user account
exsh user info <user[@server]> Show user account details
exsh group ls [@server] List groups and their members
exsh group add <group[@server]> Create a group
exsh group rm <group[@server]> Remove a group
exsh chown <spec> <nick>:<path> Change owner and/or group of a document or collection
exsh chmod <mode> <nick>:<path> Change POSIX permissions of a document or collection

Examples

# List the root of a collection
exsh ls mydata

# List a subdirectory
exsh ls mydata:reports/2025

# Print a document
exsh cat mydata:reports/2025/summary.xml

# Upload a file
exsh put report.xml mydata:reports/2025/report.xml

# Copy from remote to local
exsh cp mydata:reports/2025/report.xml ./report.xml

# Edit in place
exsh edit mydata:reports/2025/report.xml

# Delete a document
exsh rm mydata:reports/2025/old.xml

# Delete multiple documents
exsh rm mydata:reports/2025/a.xml mydata:reports/2025/b.xml

# Create a subcollection
exsh mkdir mydata:reports/2026

# Execute an XQuery script from a file
exsh exec mydata:/ -f query.xq

# Execute an XQuery script from stdin
echo 'count(collection("/db/mydata"))' | exsh exec mydata:/

# Execute without local preprocessing
exsh exec mydata:/ --no-fix -f query.xq

# List locally available XQuery validators
exsh exec --list-validators

# Push a local folder to the server (only transfers changed files)
exsh sync ./reports mydata:reports

# Pull a remote collection to a local folder
exsh sync mydata:reports ./reports

# Preview what would be transferred without doing it
exsh sync --dry-run ./reports mydata:reports

# Push and remove files on the server that no longer exist locally
exsh sync --delete ./reports mydata:reports

# Change owner and group of a document
exsh chown alice:editors mydata:reports/annual.xml

# Recursively reassign a collection tree
exsh chown -R alice mydata:reports

# Set permissions with octal mode
exsh chmod 0644 mydata:reports/annual.xml

# Add execute permission for the owner
exsh chmod u+x mydata:scripts/run.xq

# Recursively set permissions for a collection
exsh chmod -R 0644 mydata:data

Sync

exsh sync transfers only files that have changed, using a local manifest stored at ~/.cache/exsh/sync/. Direction is inferred from the argument order: local-first means push, remote-first means pull.

Change detection:

  • Push: SHA-256 hash of the local file is compared against the manifest. Same-size edits are caught.
  • Pull: last_modified timestamp from the eXist listing is compared against the manifest.

Conflicts (both sides changed since last sync) are reported and skipped — use --force to override.

Options:

Flag Effect
--force / -f Transfer all files, bypassing change detection
--dry-run / -n Show what would happen without transferring
--delete Remove files and empty folders on the destination that no longer exist on the source
--verbose / -v Also print unchanged (skipped) files
--checkpoint-every N Flush the manifest every N files (default: 100); allows interrupted syncs to resume near the point of failure

Shell completion

Generate and install tab-completion for your shell:

# bash
exsh --install-completion bash

# zsh
exsh --install-completion zsh

# fish
exsh --install-completion fish

Development

git clone https://github.com/ambs/exist-shell
cd exist-shell
uv sync
exsh --help

Run checks:

make checks   # lint, type-check, and tests
make test     # tests only

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

exist_shell-0.1.2.tar.gz (138.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

exist_shell-0.1.2-py3-none-any.whl (57.5 kB view details)

Uploaded Python 3

File details

Details for the file exist_shell-0.1.2.tar.gz.

File metadata

  • Download URL: exist_shell-0.1.2.tar.gz
  • Upload date:
  • Size: 138.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for exist_shell-0.1.2.tar.gz
Algorithm Hash digest
SHA256 cb79f0aa05705930cb526cc9568766ee040feec1bf892b342533476b28b549ca
MD5 e25629ba8ec90952f88c44270f3ce63f
BLAKE2b-256 69887af0ef258e74798e182f9dbd4089e3d3252cac0b38fa2c24639745c0a488

See more details on using hashes here.

Provenance

The following attestation bundles were made for exist_shell-0.1.2.tar.gz:

Publisher: release.yml on ambs/exist-shell

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file exist_shell-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: exist_shell-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 57.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for exist_shell-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 cb649a4caf798aaf9419733084c38b4d16e33915d5d1887bca795b8bf1dc9196
MD5 028fd80d7be66f9a7d6c36734868b66f
BLAKE2b-256 ea110d1f4bbed6806f4adcaa381666740e23ff813b5ca747cd66a7ef89d1cd33

See more details on using hashes here.

Provenance

The following attestation bundles were made for exist_shell-0.1.2-py3-none-any.whl:

Publisher: release.yml on ambs/exist-shell

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page