A bash-like shell with pipes, redirections, heredoc, environment management, and all mandatory builtins.
minishell is a group project at 42 reimplementing a simplified Unix shell inspired by bash. It handles user input, tokenisation, expansion, redirection, piped commands, and process execution — while maintaining its own environment state across commands.
Collaborators:
- bebuber — execution engine, all builtins, signal handling
- amecani — lexer/parser, tokenisation, expansion, redirection parsing, syntax check
- Interactive prompt with command history (via
readline) - Pipes (
|) connecting multiple commands - Input/output redirections:
<,>,>> - Heredoc (
<<) with delimiter support - Environment variable expansion (
$VAR,$?) - Single and double quote handling
- Signal handling:
ctrl-C,ctrl-D,ctrl-\ - Full environment management inherited from the parent process
| Command | Behaviour |
|---|---|
echo |
Prints arguments, supports -n flag |
cd |
Changes directory, supports ~ and no-arg (goes to $HOME), updates PWD and OLDPWD |
pwd |
Prints current working directory |
export |
Sets environment variables; prints declare -x format with no args |
unset |
Removes environment variables |
env |
Prints current environment |
exit |
Exits with optional status code (mod 256), validates numeric argument |
minishell/
├── minishell.h # All structs, enums, prototypes
├── Makefile
├── libft/ # Bundled libft
└── src/
├── minishell.c # Entry point, main loop, terminal setup
├── builtins/
│ ├── cd.c # cd + PWD/OLDPWD update + get_env_value
│ ├── echo.c # echo with -n flag
│ ├── env.c # env display + update_env + add_new_env + free_arr
│ ├── exit.c # exit + print_error
│ ├── export.c # export with declare -x display
│ ├── pwd.c # pwd via getcwd
│ └── unset.c # unset with env array rebuild
├── execution/
│ ├── execute.c # Dispatcher: builtin_commands, execute, alloc_env
│ ├── exe_single.c # Single command: fork, redirect, wait, restore fds
│ ├── exe_multiple.c # Piped commands: allocate pipes, fork all, wait all
│ ├── utils.c # count_commands, pipe helpers, ft_strcmp
│ └── signal.c # Signal modes: COMMAND, EXECUTION, HEREDOC
└── parsing/
├── parse.c # command_center: main parsing pipeline
├── extract_token.c # Tokeniser: quotes, operators, word boundaries
├── expand.c # $VAR and $? expansion
├── merge.c # Token merging for adjacent quoted strings
├── syntax_check.c # Validates token sequence
├── redirectioning.c # Builds t_command list from token list
├── redirectioning_v2.c # Strips redirection tokens from args array
├── redirectioning_v3_extra_fucntions.c # Opens fds for <, >, >>, <<
├── utilities.c # Token/command list traversal and cleanup
└── utilities_v2.c # Token and command initialisers
Each command cycle in command_center runs through a pipeline:
readline input
│
▼
extract_token → doubly-linked t_token list
│
▼
expand → replaces $VAR / $? in token strings
│
▼
merge → joins adjacent tokens (e.g. "hel"lo → hello)
│
▼
syntax_check → validates token sequence
│
▼
redirectioning → builds t_command linked list with args arrays
redirectioning_v2 → opens file descriptors, strips redirection tokens from args
│
▼
execute → runs single or piped commands
Single command execution saves stdin/stdout with dup, applies redirections, runs the builtin in the parent process or forks for external commands, then restores the original fds.
Multiple commands (pipes) allocates one pipe() per command, forks all children at once, adjusts each child's stdin/stdout to the correct pipe ends, then closes all pipes in the parent and waits for all children.
typedef struct s_token {
t_type type; // WORD, PIPE, IN, OUT, APPEND, H_DOC, EMPTY
char *string;
char quote; // '\'' or '"' or 0
bool merge_with_next;
struct s_token *next;
struct s_token *prev;
} t_token;
typedef struct s_commend {
char **args; // NULL-terminated args array
int fd_in; // -1 if not redirected
int fd_out; // -1 if not redirected
struct s_commend *next;
struct s_commend *prev;
} t_command;
typedef struct s_data {
char *input;
char **env;
int **fd; // pipe file descriptors
pid_t *pid;
int exit_code;
t_command *cmmds;
t_token *token;
} t_data;readlinelibrary (brew install readlineon macOS orapt install libreadline-devon Linux)
make./minishellterminal :echo "hello $USER"
hello yourname
terminal :ls -la | grep .c | wc -l
42
terminal :cat < input.txt > output.txt
terminal :export MY_VAR=hello
terminal :echo $MY_VAR
hello
terminal :exit
| Target | Description |
|---|---|
make / make all |
Build minishell binary |
make clean |
Remove object files |
make fclean |
Remove object files and binary |
make re |
fclean + all |
- Heredoc uses a temporary
h_doc.txtfile — only one heredoc active at a time. - Environment is fully copied from the parent process at startup and managed independently.
- Exit codes follow bash conventions:
127for command not found,128 + signalfor signal termination. - Written in compliance with the 42 Norm.
42 Heilbronn — Group Project