3.8 KiB
Gosh - a tiny educational shell in Go
Gosh is a small, readable shell written in Go. It’s designed as an educational project: the code favors clarity over completeness so you can learn how a shell works by reading and modifying it.
What you’ll find here:
- A minimal interactive loop that reads input, parses it into a command, and executes it.
- A simple builtin registry (e.g.,
cd,exit). - A executor that decides whether to run a builtin or spawn an external program.
- A handful of small packages (parser, prompt, paths) that keep responsibilities focused and names unambiguous.
Quick start
Requirements: Go 1.25+
- Build:
go build ./cmd/gosh - Run:
go run ./cmd/gosh
You should see a prompt like: user@host ~/path >
Type exit to quit or cd to change directories.
How a shell works (general)
At a high level, most shells follow the REPL loop (Read-eval-print):
- Read
- Display a prompt and read a line of input from the terminal (stdin).
- Parse: Split the input into a program name and its arguments. Full shells also handle quoting, escaping,
variable expansion, globbing, pipelines (
|), redirections (>,<,>>), and subshells. This project intentionally keeps parsing simple (whitespace-separated tokens) to stay readable.
- Eval (evaluate the input)
- Decide what “the command” refers to:
- Builtin: a command implemented inside the shell process (e.g.,
cd, which must change the current process’ working directory). - External: an executable resolved via
PATH(e.g.,/usr/bin/ls).
- Builtin: a command implemented inside the shell process (e.g.,
- Print (execute and print output)
- Builtins: run a function in the current process.
- External: fork/exec (in Go: create an
exec.Cmdand callRun), wiring stdin/stdout/stderr so output appears in your terminal. - Capture or propagate the exit status to use in conditionals and the next prompt.
Beyond the basics (not all implemented here):
- Redirections and pipes connect processes via file descriptors.
- Job control manages foreground/background processes and signals (Ctrl‑C, Ctrl‑Z).
- Shell scripting adds variables, conditionals, loops, and functions.
Project architecture
Executable
cmd/gosh: main program. REPL code (Read-eval-print loop)
Core packages
internal/runner: command resolver and executor. Runs either a builtin or an external program.internal/parser: turns raw input into acommand.Definition(a simple[]string).internal/command: helpers around command name/args.internal/builtins: builtin registry and implementations (currentlycd,exit).
Supporting packages
internal/prompt: expands prompt variables like%u(user),%h(host),%w(cwd).internal/paths: home-directory lookup,~expansion, and home abbreviation.
Prompt format
The prompt string is defined in cmd/gosh/gosh.go and supports:
%u→ current user%h→ hostname%w→ working directory (with home abbreviated to~)%W→ working directory (without home abbreviated to~)
Example session
$ go run ./cmd/gosh
me@myhost ~ > pwd
/home/me
me@myhost ~ > cd /tmp
me@myhost /tmp > ls
...
me@myhost /tmp > exit 0
Learning roadmap (ideas to extend)
- Input
- Support multi-line inputs.
- Parsing
- Quoting and escaping (single quotes, double quotes, backslash)
- Environment variable expansion (
$HOME,${VAR}) - Globbing (
*,?,[])
- Execution
- PATH resolution with helpful errors and
which-like behavior - Redirections (
>,>>,<) and pipelines (|) - Background jobs (
cmd &) and job control (fg/bg, signals)
- PATH resolution with helpful errors and
- UX
- Command history and line editing
- Configurable prompts/themes
Status
This is intentionally minimal and focused on readability.
If you experiment or extend it, consider adding tests close to the code you change
(e.g., for paths and parsing behavior) to keep the learning loop tight.