As beautiful as a shell
Minishell is a simplified Unix shell implementation written in C, developed as part of the 42 School curriculum. The goal is to create a functional shell that mimics the behavior of bash, handling command execution, piping, redirections, environment variables, and built-in commands — all while managing memory safely and handling signals properly.
This project teaches you about processes, file descriptors, and how a shell actually works under the hood.
| Author | GitHub |
|---|---|
| tkafanov | @timofeykafanov |
| sopperma | @SilasLovelace |
| Command | Description |
|---|---|
echo |
Display text with -n flag support |
cd |
Change the current directory (relative/absolute paths, ~, -) |
pwd |
Print the current working directory |
export |
Set environment variables |
unset |
Remove environment variables |
env |
Display the current environment |
exit |
Exit the shell with optional exit status |
- Interactive prompt displaying the current working directory
- Command history via readline (up/down arrow navigation)
- Pipes (
|) — chain commands together - Redirections
>— output redirection>>— output append redirection<— input redirection<<— heredoc (here-document)
- Environment variable expansion (
$VAR,$?) - Quote handling — single quotes (
') and double quotes (") - Signal handling —
Ctrl+C,Ctrl+D,Ctrl+\ - Exit status tracking (
$?) - Syntax error detection with meaningful error messages
The shell follows a classic pipeline architecture:
Input → Lexer → Expander → Syntax Check → Parser → Executor → Output
minishell/
├── includes/
│ ├── minishell.h # Function prototypes
│ ├── structs.h # Data structure definitions
│ └── macro.h # Constants and macros
├── srcs/
│ ├── main.c # Entry point & main loop
│ ├── lexer/ # Tokenization of input
│ │ ├── lexer.c
│ │ ├── lexer_utils.c
│ │ ├── process_token.c
│ │ └── process_token_utils.c
│ ├── expander/ # Variable & quote expansion
│ │ ├── expander.c
│ │ ├── expand_var.c
│ │ ├── expand_double.c
│ │ ├── expand_single.c
│ │ ├── merger.c
│ │ ├── expander_utils.c
│ │ └── expander_checks.c
│ ├── syntax_check/ # Input validation
│ │ ├── syntax_check.c
│ │ └── var_name_check.c
│ ├── parser/ # Token → command structures
│ │ ├── parser.c
│ │ ├── parsing_utils.c
│ │ ├── parsing_utils_2.c
│ │ └── parsing_utils_3.c
│ ├── executor/ # Command execution engine
│ │ ├── executor.c
│ │ ├── single.c # Single command execution
│ │ ├── first.c # First command in pipeline
│ │ ├── next.c # Middle commands in pipeline
│ │ ├── last.c # Last command in pipeline
│ │ ├── handle_redir.c # Redirection handling
│ │ ├── find_path.c # PATH resolution
│ │ └── ...
│ ├── builtins/ # Built-in command implementations
│ │ ├── echo.c
│ │ ├── cd.c
│ │ ├── pwd.c
│ │ ├── export.c
│ │ ├── unset.c
│ │ ├── env.c
│ │ └── exit.c
│ ├── heredoc/ # Here-document handling
│ │ ├── heredoc.c
│ │ └── heredoc_utils.c
│ └── utils/ # Memory, signals, errors
│ ├── init_memory.c
│ ├── signals.c
│ ├── freeing.c
│ ├── free_memory.c
│ ├── handle_error.c
│ └── print_error_message.c
├── libft/ # Custom C library (42 libft)
├── heredoc/ # Heredoc temp file storage
├── Makefile
└── README.md
- Lexer — Tokenizes the raw input string into a linked list of tokens (words, pipes, redirections, quotes, variables).
- Expander — Resolves environment variables (
$VAR), handles quote removal, and merges adjacent tokens. - Syntax Check — Validates token structure (e.g., no pipe at start, no dangling redirections).
- Parser — Converts the token list into a linked list of
t_commandstructures with arguments and redirections. - Executor — Forks processes, sets up pipes and redirections, resolves paths, and executes commands (builtins run in-process when possible).
- GCC or CC compiler
- GNU Make
- Readline library (
libreadline-dev)
# macOS (with Homebrew)
brew install readline
# Ubuntu / Debian
sudo apt-get install libreadline-dev
# Fedora
sudo dnf install readline-devel# Clone the repository
git clone <repository-url>
cd minishell
# Compile
make
# Run
./minishell| Target | Description |
|---|---|
make / make all |
Build the project |
make clean |
Remove object files |
make fclean |
Remove object files and binary |
make re |
Full recompile |
# Start the shell
./minishell
# You'll see a prompt with your current directory:
/Users/username/minishell$# Simple command
/home/user$ ls -la
# Pipes
/home/user$ cat file.txt | grep "hello" | wc -l
# Redirections
/home/user$ echo "hello world" > output.txt
/home/user$ cat < input.txt >> output.txt
# Heredoc
/home/user$ cat << EOF
> line 1
> line 2
> EOF
# Environment variables
/home/user$ export MY_VAR="Hello"
/home/user$ echo $MY_VAR
Hello
# Exit status
/home/user$ ls nonexistent_file
/home/user$ echo $?
2
# Quotes
/home/user$ echo "Hello $USER" # Variable expanded
/home/user$ echo 'Hello $USER' # Literal string
# Exit
/home/user$ exitThe project includes a Valgrind configuration for leak checking:
valgrind --leak-check=full \
--show-leak-kinds=all \
--track-origins=yes \
--show-error-list=yes \
--suppressions=readline.supp \
--trace-children=yes \
--track-fds=yes \
./minishellNote:
readline.suppis included to suppress known readline library leaks that are not caused by the project code.
- Process creation with
fork()andexecve() - Inter-process communication with
pipe() - File descriptor manipulation with
dup2() - Signal handling (
SIGINT,SIGQUIT,SIGPIPE) - Lexical analysis and parsing techniques
- Memory management in C (no leaks!)
- Environment variable management
- Heredoc implementation with temp files
This is a learning project and does not implement the full POSIX shell specification. Notable limitations include:
- No logical operators (
&&,||) - No subshells / command grouping with
() - No wildcards / globbing (
*) - No job control (
bg,fg,jobs) - No scripting / non-interactive mode
This project was developed as part of the 42 School curriculum. Feel free to use it as a reference, but please respect the 42 School's academic integrity rules.
Made at 42 Vienna