Skip to content

Latest commit

 

History

History
201 lines (145 loc) · 5.87 KB

BUILD_FROM_SCRATCH.md

File metadata and controls

201 lines (145 loc) · 5.87 KB

Build a Shell from Scratch — Notes + Guide

This document is a step-by-step guide that mirrors the incremental feature stages of building a small shell.

🙏 Acknowledgement: This guide is inspired by the CodeCrafters "Build your own Shell" roadmap — many thanks to the CodeCrafters team for the exercises and guidance.

The goal is to explain:

  • the theory behind each feature
  • the minimal implementation approach
  • where the final code lives in this repo

Tip: Keep each stage small. Prefer correctness and clear interfaces over feature creep.


Stage 1: Setup

1) Print a prompt

Theory: A shell is a REPL; the prompt is the UI boundary.

Code pointers:

2) Handle invalid commands

Theory: If a command isn’t found on $PATH, print a friendly error.

Code pointers:

3) Implement a REPL

Theory: Loop: prompt → read → parse → execute → repeat.

Code pointers:

4) Implement exit

Theory: Some commands must modify the shell itself, not a child process.

Code pointers:

5) Implement echo

Theory: Most builtins are pure functions: argv → stdout.

Code pointers:

6) Implement type

Theory: type tells whether something is a builtin or a path-resolved executable.

Code pointers:

7) Locate executable files

Theory: Search each directory in $PATH for an executable regular file.

Code pointers:

8) Run a program

Theory: Use fork() + execv(); parent waitpid().

Code pointers:


Stage 2: Navigation

1) The pwd builtin

Theory: pwd is just getcwd().

Code pointers:

2) The cd builtin: Absolute paths

3) The cd builtin: Relative paths

4) The cd builtin: Home directory

Theory: cd must run in the parent process or it won’t affect the shell.

Code pointers:


Stage 3: Quoting

1) Single quotes

2) Double quotes

3) Backslash outside quotes

4) Backslash within single quotes

5) Backslash within double quotes

6) Executing a quoted executable

Theory: Tokenization rules decide what becomes argv and how strings are joined.

Code pointers:


Stage 4: Redirection

1) Redirect stdout

2) Redirect stderr

3) Append stdout

4) Append stderr

Theory: Redirection is per-command file descriptor plumbing. Implement by:

  • parsing redirection operators and removing them from argv
  • using open() + dup2() before executing the command

Code pointers:


Stage 5: Autocompletion

1) Builtin completion

2) Completion with arguments

3) Missing completions

4) Executable completion

5) Multiple completions

6) Partial completions

Theory: Completion is a UI feature that queries:

  • known builtins
  • executables available on $PATH

Code pointers:


Stage 6: Pipelines

1) Dual-command pipeline

2) Pipelines with built-ins

3) Multi-command pipelines

Theory: Pipelines connect stdout of stage $i$ to stdin of stage $i+1$.

Implementation sketch:

  • create $N-1$ pipes
  • fork $N$ children
  • in child $i$:
    • dup2() stdin/stdout appropriately
    • close all pipe fds
    • run builtin or exec

Code pointers:


Stage 7: History

1) The history builtin

2) Listing history

3) Limiting history entries

4) Up-arrow navigation

5) Down-arrow navigation

6) Executing commands from history

Theory: History is two parts:

  • storage (a list of strings)
  • UI (navigation while editing the line)

Code pointers:


Stage 8: History Persistence

1) Read history from file

2) Write history to file

3) Append history to file

4) Read history on startup

5) Write history on exit

6) Append history on exit

Theory: Persistence maps to:

  • read on startup
  • write/append on demand
  • write on exit

Code pointers:


Exercises (optional)

  • Add unit tests for redirection parsing (see src/redir/redir.c for parsing/apply logic: src/redir/redir.c).
  • Extend completion to complete beyond the first token.
  • Make exit 0 exit as well (if you want bash-like behavior).