MiniShell is a Unix shell implementation written in C. It recreates the core behavior of Bash: command parsing, process execution, pipelines, redirections, environment management, signal handling, and built-in commands.
The goal of the project is to understand how Unix shells work internally — process creation, file descriptors, IPC, parsing into an AST, signal handling, and memory management.
- Features
- Architecture
- Installation
- Usage
- Testing
- Project Structure
- Dependencies
- Compatibility
- Contributing
- Interactive prompt, reads and executes commands continuously.
- Command history and line editing via GNU Readline (arrow keys, cursor movement, deleting characters).
| Command | Description |
|---|---|
echo |
Display text, supports -n |
cd |
Change the current working directory |
pwd |
Print the current working directory |
export |
Create or update environment variables |
unset |
Remove environment variables |
env |
Display environment variables |
exit |
Exit the shell with a status code |
- Execution of external programs via absolute, relative, or
PATH-resolved paths. - Proper exit status handling and propagation.
Bash-style quoting rules:
- Single quotes (
') prevent all expansion. - Double quotes (
") allow variable expansion.
minishell$ echo '$HOME'
$HOME
minishell$ echo "$HOME"
/home/userecho $USER # variable expansion
echo $? # exit status expansion| Operator | Description |
|---|---|
< |
Input redirection |
> |
Output redirection |
>> |
Append output |
<< |
Here-document |
cat < input.txt
echo hello > output.txt
cat << EOF
Hello
EOFls -la | grep minishellPipelines connect processes by wiring their file descriptors together.
| Signal | Behavior |
|---|---|
Ctrl-C |
Interrupt current command |
Ctrl-D |
Exit shell |
Ctrl-\ |
Quit running process |
-
Wildcard expansion (
*) — expanded before command execution.minishell$ ls *.c main.c utils.c parser.c -
Logical operators (
&&,||) — respect operator precedence.mkdir test && cd test false || echo "Command failed"
-
Parentheses / subshells — grouped commands run in a separate subshell environment.
(echo hello && echo world)
MiniShell processes input through a multi-stage pipeline:
User Input
│
▼
Scanner → tokenizes words, quotes, operators, pipes, redirections, parentheses
│
▼
Parser → builds an Abstract Syntax Tree (AST), applies operator precedence
│
▼
Executor → walks the AST: builtins, external commands, pipelines, redirections
Scanner — tokenizes raw input and validates it before parsing.
Parser — converts tokens into an AST that captures operator precedence, pipes, logical operators, subshells, and redirections.
Example — echo hello && (ls | grep txt):
&&
/ \
echo ()
│
PIPE
/ \
ls grep
Executor — traverses the AST and runs commands: builtin execution, forking processes, execve(), pipe wiring, redirections, subshell execution, and exit status propagation.
- C compiler (
ccorclang) - GNU Make
- GNU Readline
sudo apt-get update -y
sudo apt-get install build-essential -y
sudo apt-get install libreadline-dev -y
git clone https://github.com/jesuismarie/minishell.git
cd minishell
make
./minishellgit clone https://github.com/jesuismarie/minishell.git
cd minishell
make configure
make
./minishell| Command | Description |
|---|---|
make |
Compile MiniShell |
make clean |
Remove object files |
make fclean |
Remove object files and executable |
make re |
Recompile the project |
$ echo Hello, MiniShell!
Hello, MiniShell!
$ echo -n Hello
Hello
$ pwd
/home/user/minishell
$ cd /tmp
$ export MY_VARIABLE=value
$ echo $MY_VARIABLE
value
$ ls -l | grep ".txt"
$ cat input.txt > output.txt
$ echo World >> output.txt
$ cat < output.txt
$ cat << EOF
> Hello
> MiniShell
> EOF
Hello
MiniShell
$ env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOME=/Users/user
$ exit$ mkdir project && cd project
$ cd missing_directory || echo "Directory not found"
Directory not found$ (echo hello && echo world)
hello
world
$ (cd /tmp && pwd)The original shell's directory remains unchanged after the subshell exits.
$ ls existing_file
$ echo $?
0
$ ls missing_file
ls: cannot access 'missing_file': No such file or directory
$ echo $?
2Additional test files are provided:
cases.txt
They cover command combinations, edge cases, parser tests, error handling, redirections, and logical operators.
Compare behavior directly against Bash:
echo hello
echo "$HOME"
echo '$HOME'
export TEST=value
unset TEST
ls | grep c
cat << EOF
hello
EOF.
├── includes/
├── sources/
│ ├── built-in/
│ ├── scanner/
│ ├── parse/
│ ├── execute/
│ ├── signal/
│ ├── history/
│ ├── utils/
│ └── minishell.c
├── Libft/
├── readline-8.2/
├── cases.txt
├── Makefile
├── LICENSE
├── NOTICE
└── README.md
- C compiler: GCC or Clang
- GNU Make
- GNU Readline — command history, cursor movement, line editing
Tested on Linux and macOS, x86_64 and ARM64 (Apple Silicon).
Contributions are welcome. Before submitting changes:
- Follow the existing coding style.
- Make sure the project compiles without warnings:
make - Test your changes:
./minishell
See CONTRIBUTING.md for guidelines.