uripath command
uripath reads, writes, copies, removes and syncs local paths and URIs. It is
built on duho, which the cli extra
installs:
pip install "pathapi-uri[cli]"
Without the extra the uripath script (and python -m uripath) prints that
line to stderr and exits with status 1.
These commands run as written in an empty directory:
mkdir work
uripath write work/note.txt "hello"
uripath read work/note.txt
uripath cp work/note.txt work/copy.txt
uripath sync --dry-run --remove-missing work work-backup
uripath sync work work-backup
uripath rm --recursive work-backup
Either side can be a URI:
uripath cp input.txt ftp://host/tmp/input.txt
uripath rm --recursive ftp://host/tmp/work
uripath sync --remove-missing source/ ftp://host/target/
Use - for stdin or stdout wherever a command reads or writes bytes:
mkdir work
uripath write work/note.txt "hello"
uripath read work/note.txt > work/copy.txt
uripath write work/in.txt < work/note.txt
uripath cp - work/stdin.bin < work/note.txt
uripath cp work/note.txt - > work/stdout.bin
Commands
| Command | Behavior |
|---|---|
read PATH |
Writes PATH's bytes to stdout, in chunks. PATH may be - (stdin). |
write PATH [DATA] [--encoding ENC] |
Writes DATA encoded with ENC (default utf-8), or stdin's bytes when DATA is omitted. Replaces an existing file. PATH may be - (stdout). |
rm PATH [-r/--recursive] [--missing-ok] [--ignore-error] |
Removes a file or an empty directory; -r removes a tree. --missing-ok accepts a missing path; --ignore-error skips failures during the removal. |
cp SOURCE TARGET [-r/--recursive] [--overwrite] [--no-follow-symlinks] [--no-preserve-metadata] |
Copies a file, or a tree with -r. An existing target is refused without --overwrite, also when either side is - (which cannot be combined with -r). --no-follow-symlinks copies a link as a link; --no-preserve-metadata leaves out modes and times. |
sync SOURCE TARGET [--dry-run] [--remove-missing] [--size-only] [--no-follow-symlinks] [--no-follow-mounts] |
One-way sync with PathSyncer, comparing file content (a backend-native digest or a streamed md5), so same-size edits are copied. --size-only compares sizes only and misses same-size edits. --remove-missing deletes target entries that are not in the source. --dry-run prints each planned change without changing anything, and fails where the real run fails (a symlink onto a target that cannot hold links). --no-follow-symlinks recreates source symlinks instead of following them. --no-follow-mounts makes a mount point inside TARGET (a mounted volume, a bind mount) an error, OSError(EXDEV) and exit status 1, instead of syncing into it; the default syncs into it, as rsync does. |
Every command also takes -v, -q and --loglevel, which set how much the
uripath logger says on stderr. sync lists each change a real run makes, one
line each, while -v is given more often than -q.
A boolean option that is on by default has both spellings:
--follow-symlinks and --no-follow-symlinks, --preserve-metadata and
--no-preserve-metadata, --follow-mounts and --no-follow-mounts.
sync --dry-run and sync -v print one line per change:
would mkdir dst
would copy src/a.txt -> dst/a.txt
would remove dst/old.txt
The other actions are replace (a target entry of another type is replaced)
and symlink.
Nothing is deleted or replaced unless it is asked for by name: rm refuses a
directory with content without --recursive, sync keeps what the source
lacks without --remove-missing, cp refuses an existing target without
--overwrite. The same holds when the command is served as a tool.
Paths
An argument containing ://, or starting with a scheme that a class
registers (data:, zip:, file:, ...), is a URIPath; anything else is a
local path, so C:/data, 12:30.txt and notes:draft stay local. That is
what uripath.as_path() does. A path that starts with - is written
./-name: -- ends the options and takes no argument after it.
Exit status
| Status | Meaning |
|---|---|
| 0 | success (also --help and --version) |
| 1 | the operation failed: one line uripath: <ExceptionType>: <message> on stderr; also the cli extra not being installed |
| 2 | a wrong invocation: the usage or the problem on stderr (a missing or unknown argument, cp -r with -, an unknown --encoding) |
| 130 | interrupted with Ctrl-C |
| 141 | the reader of stdout went away (uripath read big.bin \| head), quietly, on Windows too |
Results go to stdout and diagnostics to stderr. No message carries a password or a token.
For agents
The command line describes itself and can be served as tools, with no code of
its own for either (see the duho documentation):
AGENT_HELP=1 uripath --helpprints one JSON document for every command.URIPATH_MCP=stdio uripathservesread,write,rm,cpandsyncas MCP tools whose schemas come from the fields. A tool call has no standard input:-as a source, andwritewithoutDATA, fail with a message.
From Python
uripath.cli.main(argv=None) runs one command and returns its exit status;
it needs the cli extra and does not exit the process. The commands call the
library: uripath.as_path() turns an argument into a path,
uripath.copy_stream() copies between a path and a stream, and Path.copy(),
Path.rm() and PathSyncer do the rest. Nothing the command does is reachable
only from it.