A minimal Unix shell implemented in C, developed for the 42 school curriculum. minishell mimics key behaviors of classic shells (bash, zsh), including parsing, tokenizing, environment variable expansion, pipelines, redirections, and built-in commands.
- Interactive prompt with command history
- Pipes (
|) and I/O redirections (>,<,>>,<<) - Environment variable expansion (
$VAR,$?) - Quote management (single
', double") - Built-in commands:
cd,echo,pwd,export,unset,env,exit - Execution of external programs
git clone https://github.com/leobesnard/minishell.git
cd minishell
make
./minishellYou will see:
minicheh>
Type commands as you would in a regular shell.
Tokenizing is the process of splitting the input string into meaningful tokens (words, operators, quoted strings).
- Whitespace Handling: Spaces separate tokens except inside quotes.
- Separators: Characters like
|,>,<,>>,<<are recognized as distinct tokens. - Quotes: Single (
') or double (") quoted substrings are preserved as single tokens, including spaces within. - Result: Each token is stored in a linked list with a type (WORD, PIPE, REDIRECT, etc.).
Input Command:
ls -l | grep minishell | wc -lTokens:
| Index | Value | Type |
|---|---|---|
| 0 | ls | WORD |
| 1 | -l | WORD |
| 2 | ||
| 3 | grep | WORD |
| 4 | minishell | WORD |
| 5 | ||
| 6 | wc | WORD |
| 7 | -l | WORD |
Console Output:
[minishell]$ ls -l | grep minishell | wc -l
Token 0: 'ls' (WORD)
Token 1: '-l' (WORD)
Token 2: '|' (PIPE)
Token 3: 'grep' (WORD)
Token 4: 'minishell' (WORD)
Token 5: '|' (PIPE)
Token 6: 'wc' (WORD)
Token 7: '-l' (WORD)
Input:
echo "hello | world" > out.txtTokens:
| Index | Value | Type |
|---|---|---|
| 0 | echo | WORD |
| 1 | hello | world |
| 2 | > | REDIRECT |
| 3 | out.txt | WORD |
Parsing transforms the token list into structured commands, handling syntax and grouping.
- Syntax Checking: Detects errors (e.g., consecutive pipes, missing arguments).
- Command Construction: Groups tokens between pipes as commands, identifies arguments and redirections.
- Pipeline Linking: Connects commands with pipes as needed.
For:
ls -l | grep minishell | wc -l- Command 1:
ls -l - Pipe
- Command 2:
grep minishell - Pipe
- Command 3:
wc -l
Internal Representation (C structs):
// For each command (simplified)
{
type: CMD,
arg: [ "ls", "-l" ],
rd: NULL
}
{
type: CMD,
arg: [ "grep", "minishell" ],
rd: NULL
}
{
type: CMD,
arg: [ "wc", "-l" ],
rd: NULL
}Diagram (Arrow Schema):
User input: ls -l | grep minishell | wc -l
|
v
[Tokenizing]
|
v
[Tokens: WORD, WORD, PIPE, ...]
|
v
[Parsing]
|
v
[Commands: [ls -l] | [grep minishell] | [wc -l]]
|
v
[Execution]
- Performed before parsing.
- Replaces
$VAR,$?, etc., with their values. - Respects quotes: no expansion inside single quotes.
Example:
echo $HOMEIf $HOME is /home/user, the token becomes echo /home/user.
- Built-in Commands: Run in the shell process itself.
- External Commands: Forks a new process, sets up pipes/redirections, and calls
execve. - Pipelines: Multiple processes connected via pipes.
- Redirections: File descriptors are set up per command (
<,>, etc.).
minicheh> echo "hello world"
hello world
minicheh> ls -l | grep minishell | wc -l
1
minicheh> echo "hello | world" > out.txt
minicheh> cat out.txt
hello | world
- Syntax errors (e.g.,
ls || wc):bash: syntax error near unexpected token `|` - Unclosed quotes:
Unmatching quote - Invalid commands:
bash: foobar: command not found
All memory for tokens and commands is freed after each command loop. Helper functions ensure no leaks.
This project is educational and non-production. For more information, see the source code!