Welcome to Minishell! π This is one of the most comprehensive projects from Rank 3 at 42 School, where we recreate a functional Unix shell from scratch. This project challenges us to implement command parsing, execution, signal handling, and all the core features that make a shell work.
The goal is to create a shell that behaves like bash, handling commands, pipes, redirections, environment variables, and built-in commands! π
minishell/
βββ include/minishell.h # Header file (.h)
βββ src/ # Source files (.c)
β βββ minishell.c # Main program entry point
β βββ builtins/ # Built-in commands
β β βββ cmd_cd.c # Change directory
β β βββ cmd_echo.c # Echo command
β β βββ cmd_env.c # Environment variables
β β βββ cmd_exit.c # Exit shell
β β βββ cmd_export.c # Export variables
β β βββ cmd_pwd.c # Print working directory
β β βββ cmd_unset.c # Unset variables
β βββ parse/ # Command parsing
β β βββ parse_line.c # Main parsing logic
β β βββ parse_pipeline.c # Pipeline parsing
β β βββ tokenize.c # Tokenization
β β βββ syntax.c # Syntax validation
β β βββ expand_variables.c # Variable expansion
β β βββ remove_quotes.c # Quote handling
β β βββ token_type.c # Token classification
β βββ execution/ # Command execution
β β βββ execute.c # Main execution logic
β β βββ execute_builtins.c # Built-in execution
β β βββ execute_command.c # Command execution
β β βββ execute_external.c # External commands
β β βββ get_command.c # Command resolution
β β βββ handle_redirections.c # I/O redirection
β βββ env/ # Environment management
β βββ control/ # Signal and control flow
βββ libft/ # Libft library
βββ .gitignore
βββ Makefile # Makefile to compile the project
βββ README.md # This file
echo
- Display text (with -n option support)cd
- Change directory (relative and absolute paths)pwd
- Print current working directoryexport
- Set environment variablesunset
- Remove environment variablesenv
- Display environment variablesexit
- Exit the shell (with exit codes)
- Pipes (
|
) - Chain commands together - Input redirection (
<
) - Redirect input from files - Output redirection (
>
) - Redirect output to files - Append redirection (
>>
) - Append output to files - Here documents (
<<
) - Multi-line input with delimiters - Variable expansion (
$VAR
,$?
) - Expand environment variables and exit codes - Quote handling - Single quotes, double quotes, and escaping
- Signal handling - Ctrl+C, Ctrl+D, Ctrl+\ behavior
- Command history - Navigate through previous commands
- Tokenization: Breaks input into tokens (commands, operators, arguments)
- Syntax validation: Checks for proper shell syntax
- Variable expansion: Handles
$VAR
and$?
expansions - Quote processing: Manages single and double quotes
- Pipeline creation: Builds command pipeline structures
- Built-in detection: Identifies and executes built-in commands
- External commands: Finds and executes system commands
- Pipeline execution: Handles pipes between commands
- Redirection handling: Manages input/output redirections
- Process management: Forks and manages child processes
- Variable storage: Maintains environment variables
- Variable expansion: Expands variables in commands
- Export/unset: Manages variable lifecycle
- Exit code tracking: Maintains
$?
variable
To compile the shell, run:
make
This will generate the minishell
executable.
Launch the shell:
./minishell
You'll see the custom prompt:
SMASH ->
SMASH -> echo "Hello, World!"
Hello, World!
SMASH -> pwd
/home/user/minishell
SMASH -> ls -la
# Lists directory contents
SMASH -> cd ..
SMASH -> pwd
/home/user
SMASH -> export NAME="42 Student"
SMASH -> echo $NAME
42 Student
SMASH -> env | grep NAME
NAME=42 Student
SMASH -> unset NAME
SMASH -> echo $NAME
# (empty output)
# Simple pipe
SMASH -> ls | grep ".c"
# Output redirection
SMASH -> echo "Hello" > output.txt
SMASH -> cat output.txt
Hello
# Input redirection
SMASH -> cat < input.txt
# Append redirection
SMASH -> echo "World" >> output.txt
# Here document
SMASH -> cat << EOF
> Line 1
> Line 2
> EOF
Line 1
Line 2
# Multiple pipes
SMASH -> ls -la | grep ".c" | wc -l
# Mixed redirections and pipes
SMASH -> cat file.txt | grep "pattern" > results.txt
# Variable expansion
SMASH -> export PATH_BACKUP=$PATH
SMASH -> echo $PATH_BACKUP
- Tokenization: Input is split into meaningful tokens
- Quote handling: Processes single/double quotes and escapes
- Variable expansion: Expands
$
variables and$?
exit codes - Syntax validation: Checks for syntax errors
- AST creation: Builds abstract syntax tree for execution
- Built-in check: Determines if command is built-in
- PATH resolution: Finds executable in system PATH
- Process creation: Forks processes for external commands
- Pipe setup: Creates pipes for command chaining
- Redirection: Sets up file descriptors for I/O redirection
- Signal handling: Manages interrupt signals properly
- SIGINT (Ctrl+C): Interrupts current command, shows new prompt
- SIGQUIT (Ctrl+): Ignored in interactive mode
- EOF (Ctrl+D): Exits shell gracefully
- SIGTERM: Handles termination signals
- β Interactive command prompt
- β Command history with readline
- β Search and launch executables (relative/absolute paths and PATH)
- β Handle quotes (single and double)
- β
Implement redirections (
<
,>
,>>
,<<
) - β
Implement pipes (
|
) - β
Handle environment variables (
$VAR
) - β
Handle exit status (
$?
) - β Handle signals (Ctrl+C, Ctrl+D, Ctrl+)
- β Implement all required built-ins
- β No memory leaks
- β Error handling similar to bash
The shell handles various error conditions:
- Syntax errors: Invalid command syntax
- Command not found: Non-existent commands
- Permission denied: Insufficient permissions
- File not found: Missing input files
- Pipe errors: Broken pipes
- Memory allocation: Out of memory conditions
- Signal interruption: Graceful signal handling
# Test built-ins
echo "test" | ./minishell
# Input: pwd
# Input: echo $USER
# Input: exit
# Test pipes
echo "ls | grep minishell" | ./minishell
# Test redirections
echo "echo hello > test.txt" | ./minishell
echo "cat < test.txt" | ./minishell
# Test complex pipelines
echo "ls -la | grep minishell | wc -l" | ./minishell
# Test variable expansion
echo 'export TEST="hello world"; echo $TEST' | ./minishell
# Test here documents
./minishell << 'EOF'
cat << END
line 1
line 2
END
exit
EOF
# Compare outputs
echo "command" | bash
echo "command" | ./minishell
# Outputs should be identical
- Shell Internals: Understanding how shells parse and execute commands
- Process Management: Mastering fork, exec, and wait system calls
- Signal Handling: Implementing proper signal behavior
- I/O Redirection: Managing file descriptors and pipes
- Memory Management: Preventing memory leaks in complex programs
- Error Handling: Robust error management and recovery
- Parsing Techniques: Tokenization and syntax analysis
- System Programming: Low-level Unix programming concepts
The shell features a custom colored prompt: SMASH ->
Uses GNU readline for:
- Command history navigation (β/β arrows)
- Line editing capabilities
- Tab completion (basic)
Maintains $?
variable with the exit status of the last command:
SMASH -> ls /nonexistent
ls: cannot access '/nonexistent': No such file or directory
SMASH -> echo $?
2
- Memory debugging: Use valgrind to detect memory leaks
- Signal testing: Test all signal combinations
- Edge cases: Test empty commands, multiple spaces, special characters
- Comparison testing: Compare behavior with bash
- Stress testing: Test with complex command combinations
- Parsing complexity: Handling quotes, variables, and special characters
- Signal management: Proper signal handling in different contexts
- Memory management: Preventing leaks in complex data structures
- Process synchronization: Managing multiple processes and pipes
- Error compatibility: Matching bash error messages and behavior
Made with β€οΈ at 42 Madrid