Cicada: an old school Unix shell written in Rust
An old-school bash-like Unix shell written in Rust
At a glance
- What is it?
- Cicada is a Unix shell implemented in Rust that offers the things a shell is actually used for, pipelines, redirections, command substitution, logical operators and job control, plus arithmetic you can type straight at the prompt. It suits someone who wants a small, readable shell to study or to build on, and it is a reimplementation, so the corners of bash compatibility are where you will look first.
- Who is it for?
- Adopt Cicada if you want to read a complete shell implementation, or you want a shell you can build on with a modern language and a modern parser, because the code is small and the architecture is visible. Do not adopt it as a drop in replacement for bash in a script heavy environment, since the README documents a deliberately simple feature set and compatibility gaps in the corners are where a reimplementation shows.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 62 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Cicada is
Cicada is a simple Unix shell written in Rust, and the project description calls it a simple Bash-like Unix shell with the keyword old-school.
That word old-school is doing something. There is a category of modern shell that is a scripting environment first, with prompts, completion and a rich interactive language on top. Cicada is not that. It is a shell in the sense that it starts a program, connects it to pipes, redirects its output and gives you a prompt, and it does that in a way that a person can read.
The project is MIT licensed, by Hugo Wang, and the current version is 1.3.1 with 1.3.0 released the same day and 1.2.5 two weeks earlier, so the release cadence is active. The last push to the repository was on 2026-08-22, and the crate is published on crates.io under the name cicada.
The documentation set in the README is what tells you the scope, and it is a list of the parts of a shell that people actually use. There are pages for installation, for environment variables, for the builtins, for completion, for the rc file, for history, for job control, for customising the prompt and for scripting.
A shell that ships documentation for all of those is a shell that intends to be used, and the fact that the scripting page exists at all is the difference between a toy and a program you might put in front of people.
The features, in the order the README shows them
The feature sections are ordered from basic to unusual, and the examples are real transcripts rather than descriptions.
Programs and pipelines work the way they do everywhere, including with the awkward tools.
$ ls | head -n3
Desktop
Documents
Downloads
$ echo foo,bar | awk -F "," '{print $2, $1}'
bar fooRedirections, including the combination of stderr into stdout and a pipeline into a file, are handled.
$ ls file-not-exist 2>&1 | wc > e.txt
$ cat e.txt
1 7 46Command substitution works in both the backtick and the dollar parenthesis form, which is the kind of compatibility detail that catches out a reimplementation.
$ ls -l `which sh`
-r-xr-xr-x 1 root wheel 618512 Oct 26 2017 /bin/sh
$ echo "Time is $(date)."
Time is Sun Sep 2 12:04:13 CST 2018.And the logical operators, with the semicolon for sequencing, all three of which are shown with their output so you can see the short circuit behaviour rather than infer it.
$ echo foo && echo bar
foo
bar
$ echo foo || echo bar
fooThat last example is the one to notice. The logical or stops once the first command succeeds, while the logical and runs both, which is the behaviour a user expects and the behaviour that a naive implementation gets wrong.
Arithmetic at the prompt, which is the unusual part
The last feature section is the one that distinguishes Cicada from a minimal POSIX shell, and it is the arithmetic.
$ 1 + 2 * 3 - 4
3
$ (1 + 2) * (3 - 4) / 8.0
-0.375
$ 2 ^ 31
2147483648Three things are demonstrated at once. Operator precedence is respected, since the multiplication happens before the addition and subtraction. Parentheses override it, since the second line multiplies a sum by a difference and then divides by a floating point literal, giving a negative fractional result rather than an integer one. And exponentiation is available, since the third line raises two to the thirty first power, which is a value that does not fit in a 32 bit signed integer and is a small test of the arithmetic's width.
That last point is not incidental. A shell that evaluates arithmetic has to decide how wide its integers are, and the answer here, a result well past the 32 bit boundary, suggests a 64 bit accumulator. For a shell, arithmetic at the prompt is a convenience, and the convenience becomes useful the moment you are working with sizes, offsets or counts that do not fit in 32 bits.
The implementation detail behind it is worth guessing at only from what the examples show, because the README does not describe the arithmetic engine. What the examples do establish is that the operators behave the way the documentation in the transcript implies, including precedence, grouping and floating point division.
The parsing stack and how the tool is put together
The dependency list in the manifest describes the implementation more directly than the README does.
The command line is handled by clap 4.5 with the derive feature and the help feature, alongside structopt with default features turned off. Having both is a sign of a codebase that grew from one to the other, with structopt kept for compatibility and clap used for the newer code.
The command line parser for the shell language itself is built with pest and pest_derive, which is a parser generator for grammars, and a regular expression library is there for the parts of the language that are pattern matched rather than parsed. Globbing is a separate dependency, for the cases where a command line contains wildcards.
System calls go through the nix crate with the file system, process and signal features, and the C library is bound directly for the rest. errno is a dependency in its own right, and exec is the process execution helper. The line editor is lineread, and the commented out line in the manifest shows that a local path version of it was once used in development.
The history and configuration are stored with rusqlite in bundled mode, which means the SQLite library is compiled into the binary rather than linked against the system one, and the time crate with the local offset feature handles timestamps. Configuration is read with yaml-rust, so the rc file and the environment documentation are in YAML rather than in a bespoke format.
The crate is structured as a library and a binary over the same source, with the library named cicada and the path pointing at src/lib.rs, and the binary marked as not generating documentation, so the public API reference covers the library rather than the command line tool.
The interactive half, documented page by page
A shell is more than a parser, and the documentation set covers the interactive parts one page at a time.
The rc file page is about what happens at startup and where the configuration lives. The environment variables page covers the variables the shell itself reads and sets, which for a shell includes the prompt variables and the path handling. The prompt page is about customising the prompt, and the history page about how command history is kept, which given the SQLite dependency is a database rather than a file.
Completion is the page that decides whether a shell feels modern or archaic, and it is documented separately from the builtins, which suggests it is implemented as a layer over them rather than inside them. The builtins page is the reference for the commands the shell provides itself, and the scripting page is the one for people writing scripts rather than typing at the prompt.
Job control has its own page, and that is the feature that separates a shell you can run long processes in from one you cannot. A shell that cannot stop a foreground job, resume it in the background and list what is running is not usable for the kind of work a shell is wanted for.
The existence of these pages, written separately and linked from a table in the README, is a sign of a project that is documenting what it has rather than what it plans to have.
The build, the tests and the state of the project
The Makefile is a short one and it shows the working rhythm.
The run target builds the debug binary and starts it with the list flag, which is the shell printing the commands it supports, which is a quick way to confirm the build works. The install target does a release build and copies the binary into the local bin directory. There is a documentation target that runs cargo doc and opens it, and a test target that runs the binary tests.
run:
@rustc -V
cargo build
./target/debug/cicada -l
install:
cargo build --release
cp target/release/cicada /usr/local/bin/The scripting-test target is the interesting one, because it runs a shell script in the tests directory against the built binary, so the shell is tested by running scripts through it. That is the right way to test a shell, since the interesting behaviour is the interpretation of commands rather than any function.
The lint setup is worth a mention too. The clippy target passes two allowances, one for needless returns and one for pointer arguments, which are the two lints that a shell implementation tends to trip over and that the author has decided to live with. The configuration lives in a .clippy.toml file next to a .rustfmt.toml, so the formatting and the lint policy are both pinned in the repository rather than left to a global setting.
The release profile is configured for a small binary, with link time optimisation and symbol stripping enabled, and the project keeps a changelog, a clippy configuration, a rustfmt configuration and a miscellaneous directory at the root, so the repository is tidy for something of this size.
Editorial conclusion
Adopt Cicada if you want to read a complete shell implementation, or you want a shell you can build on with a modern language and a modern parser, because the code is small and the architecture is visible. Do not adopt it as a drop in replacement for bash in a script heavy environment, since the README documents a deliberately simple feature set and compatibility gaps in the corners are where a reimplementation shows. Before you rely on it, read the builtins and scripting documentation, because those two pages decide whether your existing scripts will run at all.
Frequently asked questions
What is Cicada?
It is a simple Unix shell written in Rust, described as a Bash-like Unix shell, with a crate published on crates.io under the name cicada and an MIT licence. It supports pipelines, redirections, command substitution in both backtick and dollar parenthesis forms, the logical operators, job control and arithmetic evaluated directly at the prompt.
Which shell features does Cicada support?
The documented features cover running programs and pipelines, redirections including redirecting standard error into standard output, command substitution, sequencing with the semicolon and the logical and and or operators, and math arithmetic entered directly in the shell with precedence, parentheses and exponentiation.
How is Cicada built and tested?
It is a Cargo project with a library and a binary over the same source, built on pest for the command grammar, clap and structopt for the command line, nix for system calls, lineread for line editing and rusqlite in bundled mode for history and configuration. The Makefile has a test target running the binary tests and a scripting-test target that runs a script through the built shell.
What does Cicada use for its configuration and history?
Configuration is read with yaml-rust, so the rc file and the environment settings are in YAML, and history is kept with rusqlite in bundled mode, which compiles SQLite into the binary rather than linking the system library. The README documents the rc file, the environment variables, the history, the prompt, completion, the builtins, job control and scripting in separate pages.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mitnk-cicada)Was this useful?