esh - Embeddable Shell
Note: This is pre-release / alpha software, use at your own discretion. Notably, at the time of writing, the actual REPL mode is missing, as well as sufficient testing, panic-safety, and library documention.
A Rust library for building interactive, command-driven CLI applications. esh provides a shell framework that handles argument parsing, command dispatch, VFS integration, and structured logging -- so you can focus on defining commands rather than wiring up boilerplate.
The crate ships both a library (esh) and a reference binary (esh)
that demonstrates how to assemble a working shell from the library's building
blocks.
Library
The library is backend-agnostic. It has no dependency on any concrete VFS
crate -- it defines a Vfs trait that consumers implement.
Modules:
shell-- The core framework.ShellConfigis a builder that registers CLI arguments, subcommands, command handlers, and an optional VFS lookup. Calling.build()produces anArc<dyn Shell>that can be.run()'d. Built-in commands includeversion,exit, and (when a VFS is configured)pwd.parse-- A POSIX-like shell parser.shell_parse_line()splits a string into words honoring single quotes, double quotes, backslash escapes,#comments, and line continuations.shell_parse_arg()processes escape sequences in a single argument. Supported escapes include\n,\t,\xHH(hex bytes),\u{H..H}(Unicode scalars), and\0ooo(octal).util-- Logging initialization viatracing+tracing-subscriberwithENV_FILTERsupport, thedie!macro for fatal exits, andpluralize!for simple English pluralization.
Key traits:
Shell-- Run a shell fromenv::args()or a supplied arg iterator.Vfs-- Backend-agnostic virtual filesystem interface (fn cwd(&self) -> &Path). Implement this for any FS backend you like.
Binary
The reference binary demonstrates how to wire up the library:
- Defines a
-p/--pathCLI argument that validates and canonicalizes a directory path. - Implements
Vfsforvfs-kit'sDirFSvia a thinDirFsVfsnewtype adapter. - Registers a VFS lookup function that creates the
DirFSfrom the parsed path. - Builds and runs the shell.
Usage
# Run a command against a directory
# Print version
# Verbose logging (-v, -vv, -vvv for increasing detail)
# Quiet mode (errors only)
Shell Parser
The parser in parse.rs follows POSIX shell quoting conventions:
| Syntax | Behavior |
|---|---|
word |
Plain word, split on whitespace |
'...' |
Single-quoted literal (no escape processing) |
"..." |
Double-quoted (backslash escapes active) |
\ |
Escaped space (joins words) |
\n, \t, ... |
Standard C escapes |
\xHH |
Hex byte (1-2 digits) |
\u{H..H} |
Unicode scalar (1-6 hex digits) |
\0ooo |
Octal byte (up to 3 digits) |
# comment |
Line comment (only at word boundary) |
\ + newline |
Line continuation |
use ;
let words = shell_parse_line?;
// => ["echo", "hello world", "foo bar"]
let arg = shell_parse_arg?;
// => "Hello"
Extending with Custom Commands
Use the ShellConfig builder to register your own arguments, subcommands, and handlers:
use Arc;
use ;
let cfg = shell_config!
.cli_args
.cli_cmds
.cli_handler;
let sh = cfg.build;
sh.run;
Plugging in a VFS Backend
The library's Vfs trait is intentionally minimal so you can bring any filesystem backend:
use Path;
use Vfs;
Register it via .vfs_lookup() on ShellConfig:
let cfg = shell_config!
.vfs_lookup;
When a VFS is configured, the shell automatically enables the vfs-aware command,
e.g. pwd.
Building
Authors
This library is written by Michael Wildpaner.
Acknowledgments
This project is made possible thanks to the incredible work of the Rust community and the maintainers of the following crates:
- clap – For providing the gold standard in command-line argument parsing.
- tracing – For structured, scoped, and async-aware diagnostics.
- vfs-kit – For the elegant virtual file system abstractions so I don't have to.
I'm grateful to the authors and contributors of these libraries for their dedication to the Rust ecosystem.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.