- C 94.2%
- Makefile 5.8%
|
|
||
|---|---|---|
| .github/workflows | ||
| includes | ||
| lib/libft | ||
| src | ||
| .gitignore | ||
| .gitmodules | ||
| .valgrindignore.txt | ||
| Makefile | ||
| README.md | ||
minishell
A minimalist reimplementation of a Unix shell, written in C as part of the 42
curriculum. It reproduces the core behaviour of bash in an interactive
session: a read-eval-print loop, command execution with PATH resolution,
pipelines, redirections, here-doc, env var expansion, quote
handling and, a set of built-in commands.
The project is constrained by the 42 C Cursus coding standard (the Norminette): Using only C99, no global variables except a single one reserved for signal handling, functions limited to 25 lines, and a restricted set of allowed libc functions, etc...
Features
Interpreter
- Interactive prompt built on GNU
readline, with command history. PATHlookup for executables, and direct execution of absolute and relative paths.- Correct propagation of exit status, exposed through
$?.
Parsing
The input line goes through a small, staged pipeline:
- Expansion — substitutes
$VARand$?, respecting quoting rules (no expansion inside single quotes). - Tokenization — splits the expanded line into raw tokens.
- Lexing — assigns a type to each token (command, argument, pipe, redirection, redirection target), strips quotes, and rejects invalid operators.
- Command building — groups tokens into a list of commands separated by pipes, resolves each command path, and opens the associated redirection targets.
Single quotes (') and double quotes (") are supported, including their
different effects on expansion.
Redirections and pipes
- Input redirection:
< - Output redirection (truncate):
> - Output redirection (append):
>> - Here-document:
<<(with expansion unless the delimiter is quoted) - Pipelines of arbitrary length:
cmd1 | cmd2 | cmd3 | ...
Built-in commands
Implemented as internal functions rather than external binaries:
| Builtin | Description |
|---|---|
echo |
Print arguments, with support for the -n option |
cd |
Change directory (absolute/relative path, ~, HOME) |
pwd |
Print the current working directory |
export |
Add or update environment variables; print sorted env |
unset |
Remove environment variables |
env |
Print the environment |
exit |
Leave the shell with an optional status code |
Signals
Signal behaviour mirrors an interactive bash session, with distinct
configurations for the prompt, the execution phase, forked children and
built-ins:
Ctrl-C(SIGINT) at the prompt clears the current line and redisplays a fresh prompt; during a running command it interrupts the child (status 130).Ctrl-\(SIGQUIT) is ignored at the prompt and default in children (status 131).Ctrl-D(EOF) on an empty line exits the shell.
Per the Norm, a single global variable is used, and only to carry the last received signal number.
Architecture
src/
├── minishell.c Entry point
├── shell/prompt.c Read-eval-print loop and prompt rendering
├── parsing/ Expansion, tokenizer, lexer, command builder
├── commands/
│ ├── exec.c Pipeline execution: fork, pipe, dup2, wait
│ ├── builtins/ echo, cd, pwd, export, unset, env, exit
│ └── operands/ Redirections (<, >, >>, <<) and operator checks
├── env/ Environment access, expansion, sorting, PWD update
├── signals/ sigaction setups and handlers per execution context
├── errors/ Error reporting helpers
└── utils/ PATH resolution, fd management, temp files
- Hand made dynamic vector. A generic growable-array module from
libftmade during the cursus is used throughout: for the command list, argument lists, token lists, and even for building strings character by character during expansion. This keeps memory handling uniform and centralises cleanup. - Staged parsing. Each parsing stage has a single responsibility and a clear input/output contract, which makes the flow from raw line to executable command list easy to follow and to free on error.