This repository contains a minimal POSIX-style shell implemented in Rust as part of CodeCrafters' "Build Your Own Shell" challenge. It features a readline-powered REPL, a small set of built-in commands, external command execution, pipelines, output redirection, and simple tab completion.
Challenge details: https://app.codecrafters.io/courses/shell/overview
The shell starts an interactive loop, reads a line, parses it into one or more commands (a pipeline), and then executes either built-ins or external programs. It supports:
- Interactive prompt with history and completion
- Built-in commands:
cd,echo,exit,pwd,type,history - External commands resolved via
PATHor absolute paths - Pipelines (
cmd1 | cmd2 | ...) - Output redirection for stdout, stderr, and both together
- History persistence via
HISTFILE
src/main.rs- Entry point. Sets up the readline editor, loads/saves history, drives the REPL loop, invokes parsing, orchestrates pipelines, wiring of stdin/stdout/stderr and delegating to built-ins or external processes.
src/parser.rs- Tokenizer and parser for a single input line. Produces a vector of
ParsedCommandstructs forming a pipeline. Handles quoting rules, backslash escapes inside and outside quotes, pipe splitting, and output redirection targets/flags. - Constants used across the shell (prompt string, command names, environment variable names, file-descriptor tokens like
1,2, and&). - Escape expansion helper used by
echo -e.
- Tokenizer and parser for a single input line. Produces a vector of
src/commands.rs- Implementations of built-in commands and the external command runner.
- Built-ins implemented:
cd [dir]— changes directory. Defaults to$HOME. Interprets~as home.echo [-e] [args...]— prints arguments; with-eexpands\n,\t,\r,\\,\0,\",\'.exit [code]— terminates the shell with an optional numeric exit code (default 0).pwd— prints the current working directory.type <name>— reports whether<name>is a shell builtin or the full path of an external command.history [N] | -r <file> | -a <file> | -w <file>— prints recent history, reads entries from a file, appends only new entries, or writes the full history respectively.
- External command execution:
- Resolved via
$PATHusingsearch_executable, or uses an absolute path if executable. - Supports capturing stdout/stderr when redirected, otherwise inherits the terminal streams.
- For single commands, reads child pipes and forwards data; for pipelines, spawns chained processes and waits.
- Resolved via
- Output redirection helper: opens files in truncate or append mode and returns a writer when redirection is requested.
src/shell_helper.rs- Glue code for
rustyline: helper and completer implementations. - Tab-completion of built-ins and available
PATHexecutables (deduped and sorted).
- Glue code for
- Quoting and escaping
- Single quotes preserve literal text.
- Double quotes allow certain backslash-escaped characters (e.g.,
\",\\,\`,$,!). - Outside quotes,
\escapes the next character.
- Pipelines
- The input is split on unescaped, unquoted
|into a sequence ofParsedCommands.
- The input is split on unescaped, unquoted
- Redirection
1> fileredirects stdout,2> fileredirects stderr,&> fileredirects both.>>sets append mode; a single>truncates.
- History
- Uses
rustylinein-memory history. IfHISTFILEis set, the file is loaded on startup and written back on exit.history -aappends only the new entries since the last write,history -wrewrites the whole file, andhistory -rloads entries from a file.
- Uses
Prerequisites: Rust toolchain (edition 2021; see Cargo.toml for rust-version).
- Run with Cargo:
cargo run
- Or via the helper script used by CodeCrafters runners:
./your_program.sh
- External command:
$ ls -la
- Pipeline:
$ ls | grep rs | wc -l
- Redirection:
$ echo hello > out.txt $ &> errors_and_output.log ls /no/such/path $ echo append >> out.txt
- Built-ins:
$ pwd $ cd ~/projects $ echo -e "line1\nline2" $ type echo $ history 20
Defined in Cargo.toml:
rustyline— line editing, history, completion. Features enabled:with-file-history,derive.os_pipe— portable OS pipe creation used for pipeline wiring.
- This is an educational implementation focusing on clarity over complete POSIX compliance.
- Job control, environment variable expansion, globbing, subshells, and advanced redirection are not implemented.
- Behavior may differ from
bash/zshin edge cases, quoting/escaping rules, and error handling.
See the repository license (if any) or treat this as example code for educational purposes.