minishell
π§ Project Overview
Minishell is a Unix-like shell implemented from scratch as part of the 42 Madrid curriculum by students m-allera and frromero. The primary objective is to recreate the core functionalities of a real shell, such as bash or zsh, using only low-level C system calls and standard libraries β without relying on external libraries or shortcuts.
This project is a deep dive into system programming, focusing on how Unix-based shells interpret commands, spawn and manage processes, handle input/output streams, manage memory, and interact with the terminal interface. Itβs not just about getting commands to run; itβs about understanding the engine behind the command line.
Working in pairs simulates a real development environment and emphasizes version control, teamwork, and clean code architecture.
π Key Functionalities
β Command Parsing and Execution
Minishell interprets and executes both built-in and external commands.
Built-in Commands:
echo: Displays arguments to standard outputcd: Changes the current working directorypwd: Prints the current directory pathexport: Sets environment variablesunset: Removes environment variablesenv: Lists current environment variablesexit: Exits the shell, optionally with a given status code
These commands are executed internally, bypassing process creation to remain compliant with standard shell behavior and improve performance.
External Commands:
Minishell locates binaries using the PATH environment variable, then forks and executes them via execve(), mimicking how a real shell handles external programs.
ls -la | grep ".c" > output.txt
π§΅ Process Management
Process control is at the heart of a shell. Minishell uses:
fork()to create child processesexecve()to execute external binarieswait()/waitpid()to manage process lifecycles and collect their exit status
Each pipe segment and command execution runs in its own process, requiring precise management of file descriptors, error handling, and resource deallocation.
π Input/Output Redirection and Pipes
The shell supports:
- Input Redirection (
<) - Output Redirection (overwrite) (
>) - Output Redirection (append) (
>>) - Pipelines (
|)
Behind the scenes, this involves duplicating file descriptors using dup2(), setting up communication through pipe(), and carefully orchestrating multiple processes to simulate stream chaining β all while preserving isolation between commands.
< input.txt grep "error" | sort | uniq > results.log
π§© Lexing, Parsing, and Environment Management
A key part of shell development is lexical analysis and parsing:
- Tokenization: Commands are split based on whitespace and special characters (e.g.
|,<,>) - Quote Handling: Properly supports single (
') and double (") quotes, ensuring argument grouping - Variable Expansion: Environment variables like
$HOMEor$USERare expanded before execution - Escape Sequences: Properly handles backslashes and nested expansions
This is all done manually, without using tools like lex, yacc, or readline, requiring the implementation of a custom parser.
π¦ Signal Handling
Minishell handles terminal signals to ensure smooth user interaction:
- SIGINT (
Ctrl+C): Interrupts a running process but not the shell - SIGQUIT (
Ctrl+\): Suppressed as in modern shells - EOF (
Ctrl+D): Exits the shell gracefully when received on an empty prompt - Custom Prompt Behavior: Ensures that signals donβt break the prompt layout or logic
Signal behavior is carefully controlled using signal() or sigaction() to simulate a real terminal session.
π§Ό Memory Management
Minishell adheres to strict memory hygiene:
- All dynamic memory allocations (
malloc) are freed appropriately - Avoids memory leaks and double frees
- Proper management of environment variables in a dynamic array
- Reinitializes memory and file descriptors for each command cycle
Memory checks are continuously validated using tools like Valgrind or AddressSanitizer.
π Architecture Summary
The shell is structured in modular components:
| Module | Purpose |
|---|---|
lexer/ | Tokenizes input strings |
parser/ | Builds command structures and handles syntax rules |
executor/ | Handles forks, redirections, and command execution |
builtins/ | Built-in command implementations |
env/ | Environment variable management |
signals/ | Signal trapping and user interruption handling |
utils/ | Helper functions (string ops, error printing, etc.) |
π Example Use Cases
minishell$ echo "Welcome to Minishell"
Welcome to Minishell
minishell$ export USER=fran
minishell$ echo $USER
fran
minishell$ cat file.txt | grep "42" | wc -l > count.txt
π§ͺ Testing and Limitations
While Minishell implements many core features, it does not (yet) include:
- Job control (
fg,bg) - Background processes (
&) - Subshells or command substitution (
$(...)) - Wildcards (
*) - Here-docs (
<<)
Each implementation is rigorously tested against expected shell behavior and edge cases, and the project passes all functional criteria defined in the 42 subject.
π Conclusion
Minishell is a comprehensive systems-level project that demonstrates:
- Deep understanding of Unix internals
- Mastery of process control and inter-process communication
- Proficiency in memory and file descriptor management
- Ability to write robust, modular, and maintainable C code
It bridges the gap between application-level programming and operating system fundamentals β an essential milestone in any low-level programming journey.