A robust, lightweight shell implementation written in C. This project re-implements the core interactive behavior of bash (Bourne Again SHell) with a focus on process management, file descriptor manipulation, signal handling, and environment management.
Scope note (42 minishell): This is an educational re-implementation of a subset of a POSIX-like shell. It is not intended to be a drop-in replacement for
bash.
- Overview
- Features
- Supported Syntax
- Built-ins
- Architecture
- Installation
- Usage
- Project Structure
- Testing & Debugging
- Limitations
- Contributing
- License
minishell is a small interactive shell that:
- reads and parses user input (including quotes and expansions),
- builds an execution plan (commands, pipes, and redirections),
- runs external programs via
fork()/execve()with correct I/O wiring, - implements common shell built-ins that must run inside the shell process,
- tracks exit status and exposes it via
$?.
The codebase is split into parsing and execution layers to keep responsibilities clear and to make complex behaviors (pipelines, redirections, heredocs) maintainable.
-
Execute binaries from:
$PATHresolution- absolute paths (
/bin/ls) - relative paths (
./a.out)
-
Correct process lifecycle using
fork()+execve() -
Proper exit-code propagation (including pipeline exit rules)
- Single quotes (
'...') prevent interpretation/expansion - Double quotes (
"...") allow variable expansion while preserving spaces - Environment variable expansion:
$VAR,$?
- Pipe:
| - Input:
< - Output truncate:
> - Output append:
>> - Heredoc:
<<(reads until a delimiter)
This minishell supports a practical subset of common shell syntax:
ls -la | grep "\.c" | wc -lcat < infile
cat < infile > outfile
cat < infile >> outfilecat << EOF
hello $USER
EOFecho "Hello $USER" # expands
echo 'Hello $USER' # no expansionfalse
echo $?Built-ins are implemented internally because they must affect the shell state (environment, current directory, exit code):
cd— change directory (supports relative/absolute paths and~when applicable)echo— print text (supports-n)pwd— print current working directoryexport— set environment variablesunset— remove environment variablesenv— display environment variablesexit— terminate the shell with an optional status code
The project is organized into two main components: Parsing and Execution.
A custom lexical analyzer converts the input line into structured tokens and command nodes:
- Tokenization: categorizes input as
WORD,PIPE,REDIR, etc. - Segmentation: quote-aware segmentation allows mixed strings (e.g.,
echo "Hello"world) - Expansion: expands
$VARand$?before execution (with quote rules)
The execution layer traverses the parsed command list and performs:
- Pipeline creation using UNIX pipes
- Redirection chaining using
open()+dup2() - Process spawning using
fork()andexecve() - Wait / exit status collection for correct
$?behavior
- A POSIX-like environment (Linux or macOS)
makegccorclangreadlinedevelopment headers
git clone https://github.com/Neko-Bytes/minishell
cd minishell
makeIf your system does not ship
readlineheaders by default, install the development package (e.g.,libreadline-devon Debian/Ubuntu,readlineon macOS via package manager).
Run the shell:
./minishellTypical usage:
minishell$ echo "hello" | cat -e
minishell$ export NAME=world
minishell$ echo "$NAME"
minishell$ cat << EOF
> line 1
> line 2
> EOFExit:
minishell$ exitHigh-level layout (names may vary slightly by branch):
.
├── include/ # Public headers
├── libft/ # 42 libft dependency (vendored)
├── parsing/ # Lexer/parser/expansion logic
├── src/ # Execution engine + built-ins + runtime
├── testdir/ # Local testing assets/scripts (if present)
├── outfiles/ # Output artifacts for tests (if present)
├── Makefile
└── README.md
A reliable way to validate behavior is to compare outputs and exit statuses side-by-side:
# bash
bash$ echo "a b" | wc -w
bash$ echo $?
# minishell
minishell$ echo "a b" | wc -w
minishell$ echo $?-
Use Valgrind to verify memory and FD hygiene:
valgrind --leak-check=full --track-fds=yes ./minishell
-
Validate signals interactively:
Ctrl-Cshould interrupt and show a fresh promptCtrl-Dshould exit the shell (EOF)Ctrl-\should not kill the shell at the prompt
This project intentionally focuses on the 42 minishell scope. Commonly out-of-scope features include:
- Job control (
fg,bg,jobs) - Shell scripting (
if,for,while, functions) - Wildcard/globbing (
*) and advanced pattern expansion - Subshells and command substitution (
$(...), backticks)