Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

224 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🐚 minishell

C 42 School License: MIT

A minimal, POSIX‑inspired Unix shell built from scratch in C with zero external dependencies beyond the standard library, readline, and a custom libft.

This is the final version of the 42 minishell project. It reads user input, breaks it into tokens, builds a command pipeline, expands environment variables and quotes, sets up file redirections, and executes commands with forks, pipes, and execve — just like bash, only smaller.


✨ What it does

Feature Description
Interactive prompt Colored prompt with user, hostname, and current directory.
Command execution Runs external programs via $PATH resolution or absolute/relative paths.
Pipes Chains commands with |, e.g. ls -l | grep .c | wc -l.
Redirections <, >, >>, and << (heredocs) all supported.
Environment Full env, export, unset, and $VAR expansion including $?.
Quotes Handles single and double quotes, plus quote-aware expansion.
Builtins echo, cd, pwd, export, unset, env, exit.
Signals Ctrl-C and Ctrl-\ behave correctly in the prompt, during execution, and inside heredocs.

🏗️ Architecture overview

The shell is organized into three clean layers that mirror how a real shell works:

┌─────────────────────────────────────────────────────────────┐
│  REPL loop (main.c)                                         │
│  readline → lexer → parser → executor → cleanup             │
└─────────────────────────────────────────────────────────────┘
                              │
        ┌───────────────────────┼───────────────────────┐
        ▼                       ▼                       ▼
   ┌─────────┐            ┌──────────┐           ┌───────────┐
   │  Lexer  │            │  Parser  │           │ Executor  │
   │         │            │          │           │           │
   │ chars   │  tokens    │ command  │ pipeline  │ fork/     │
   │  ──►    │  ───────►  │  list    │  ──────►  │ execve/   │
   │ tokens  │            │          │           │ builtins  │
   └─────────┘            └──────────┘           └───────────┘
  1. Lexer — turns raw input into a flat list of tokens (COMMAND, PIPE, IN, OUT, HEREDOC, APPEND, quotes, etc.).
  2. Parser — joins arguments and redirections, expands $VAR and quotes, splits pipelines, and wires up file descriptors.
  3. Executor — forks child processes, redirects stdin/stdout, executes builtins or external binaries, waits for everything to finish, and stores the exit code.

🧠 Design patterns & core concepts

Even though the project is written in C, it applies several classic software-engineering ideas:

1. Singleton — one source of truth

Functions like data(), env(), and pipeline() return pointers to static variables. This guarantees a single, globally accessible instance of the shell state, environment table, and pipeline context without scattering globals across files.

2. Object‑oriented C — a hash table with methods

The custom hash table (t_hash) is a self‑contained "object": it carries its own function pointers for add, get_value, delete, clean, hash, and print. You interact with it through a consistent interface rather than directly touching its internal buckets.

3. Strategy / Command pattern — builtin dispatch

Builtins are registered in a t_builtins struct that holds a function pointer per command (cd, pwd, env, echo, exit, unset, export). The executor picks the right strategy at runtime based on the command name, making it easy to add or swap builtins.

4. Pipeline pattern — composable command chains

Pipelines are split into segments, each segment gets its own pipe(), and stdin/stdout are rewired between commands. This lets simple commands be composed into powerful chains without hard‑coding special cases.

5. State machine — lexer

The lexer walks the input character by character and transitions between states: normal text, inside single quotes, inside double quotes, or while parsing a redirection. This keeps quote and operator handling predictable.

6. RAII‑style cleanup

Every resource that is opened (file descriptors, allocated lists, hash tables, command nodes) has a matching cleanup path. A central data_destroy() routine tears the whole shell state down cleanly on exit.

7. Layered architecture

Lexer, parser, and executor each own one concern and communicate through well‑defined data structures (t_list of tokens → t_command list → pipeline execution). This separation makes the codebase easier to read, test, and extend.


🚀 Build & run

# clone with the custom libft submodule
git clone --recursive git@github.com:Filz0r/minishell.git
cd minishell

# compile
make

# run
./minishell

Other useful targets:

Target What it does
make clion Rebuild with debug symbols (-g -g3).
make valgrind Run under Valgrind with leak and FD tracking.
make clean Remove object files.
make fclean Remove object files and the binary.
make re Full rebuild.

🛡️ Why it matters

Building a shell from the ground up teaches the fundamentals that higher‑level languages hide:

  • Process managementfork, execve, waitpid, and process groups.
  • File descriptors & inter‑process communication — pipes, dup2, and careful FD bookkeeping.
  • Memory safety in C — every allocation has an owner and a cleanup path.
  • Signal handling — asynchronous events that must not corrupt shell state.
  • Parsing & language design — tokenization, quoting rules, and expansion order.

For non‑technical readers: think of this project as building a tiny operating‑system assistant. It takes the words you type, understands their structure, starts the right programs, connects their inputs and outputs, and reports back whether they succeeded.


📜 License

This project is released under the MIT License.


Made with ☕, 🧠, and a lot of printf debugging at 42 Lisboa.

About

This is the repo for the 42 project minishell, git gud suckers

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages