#shell #console #interactive #terminal #command


A library for creating interactive command line shells in Rust

6 releases

0.0.7 Aug 25, 2019
0.0.6 Sep 29, 2018
0.0.4 Sep 11, 2016
0.0.3 Aug 29, 2016
0.0.1 Jun 19, 2016

#16 in #interactive

Download history 34/week @ 2022-11-25 32/week @ 2022-12-02 109/week @ 2022-12-09 37/week @ 2022-12-16 70/week @ 2022-12-23 23/week @ 2022-12-30 62/week @ 2023-01-06 18/week @ 2023-01-13 71/week @ 2023-01-20 55/week @ 2023-01-27 95/week @ 2023-02-03 39/week @ 2023-02-10 107/week @ 2023-02-17 61/week @ 2023-02-24 131/week @ 2023-03-03 55/week @ 2023-03-10

356 downloads per month
Used in 4 crates

MIT license

316 lines

License Build Status Coverage Status Crates.io


Rust library to create interactive command line shells


Copyright © 2019 Pierre-Henri Symoneaux

Check LICENSE.txt file for more information.

This is currently a work in progress, and the API should be consider unstable. I'll start documenting and releasing to crates.io once a first level of stability has been reached

How to use


More often, you will include the library as a dependency to your project. In order to do this, add the following lines to your Cargo.toml file :

shrust = "0.0.7"

Basic usage

Let's have a look at example dummy.rs :

extern crate shrust;
use shrust::{Shell, ShellIO};
use std::io::prelude::*;

fn main() {
    let mut shell = Shell::new(());
    shell.new_command_noargs("hello", "Say 'hello' to the world", |io, _| {
        writeln!(io, "Hello World !!!")?;

    shell.run_loop(&mut ShellIO::default());

The output of this program would be

λ cargo run --example dummy
     Running `target\debug\examples\dummy.exe`
 hello    :  Say 'hello' to the world
 help     :  Print this help
 history  :  Print commands history or run a command from it
 quit     :  Quit
Hello World !!!

Attaching data

You can attach data to the shell for usage by commands as seen in data.rs:

let v = Vec::new();
let mut shell = Shell::new(v);
shell.new_command("push", "Add string to the list", 1, |io, v, s| {
    writeln!(io, "Pushing {}", s[0])?;
shell.new_command_noargs("list", "List strings", |io, v| {
    for s in v {
        writeln!(io, "{}", s)?;

shell.run_loop(&mut ShellIO::default());


λ cargo run --example dummy
     Running `target\debug\examples\dummy.exe`
 help     :  Print this help
 history  :  Print commands history or run a command from it
 list     :  List strings
 push     :  Add string to the list
 quit     :  Quit
>push foo
Pushing foo
>push bar
Pushing bar

Using custom I/O

In previous examples, the shell's loop was run the following way:

shell.run_loop(&mut ShellIO::default());

ShellIO::default() returns an stdin/stdout IO.

It's possible to create a ShellIO instance around user-defined I/O. For example to connect a Shell on a socket, the ShellIO would be created with

let mut io = ShellIO::new_io(sock);

where sock is the socket, then the shell can be started with

shell.run_loop(&mut io);

This is applied in example socket.rs.

Default handler

By default, when a command is not found, the evaluation returns an UnknownCommand error. This behavior can be customized by providing a custom default handler to be invoked on not found command.

let mut shell = Shell::new(());
shell.set_default(|io, _, cmd| {
    writeln!(io, "Hello from default handler !!! Received: {}", cmd)?;
shell.run_loop(&mut ShellIO::default());


λ cargo run --example default
     Running `target\debug\examples\default.exe`
Hello from default handler !!! Received: foo

This is applied in example default.rs.


A shell instance itself cannot be shared across threads, it needs to be cloned. A shell is clonable only if the wrapped data is clonable too. However, the wrapped data can be easily shared if (for example) it's an Arc around a Sync+Send value.


Additional examples are provided in documentation and in examples directory


~29K SLoC