This project has been created as part of the 42 curriculum by cacesar- and clados-s.
Implementation of a mini shell in C, inspired by POSIX shell behavior, focusing on process control, pipeline execution, I/O redirections through file descriptors, and interactive command input using readline.
This project directly exercises core shell system calls (syscalls): process creation with fork(), program execution with execve(), synchronization with waitpid(), inter-process communication with pipe(), and input/output manipulation via open(), dup2(), and close(). It also includes signal handling (e.g., SIGINT) to keep the interactive experience stable.
- External command execution via
PATHlookup andexecve(). - Pipelines with
|(chained commands usingpipe(),dup2(), andfork()). - Redirections:
<,>(truncate),>>(append), and heredocs<<(temporary.heredoc_Nfiles). - Variable expansion:
$VAR, plus$?,$$, and$#as implemented in the lexer. - Built-ins:
echo,cd,pwd,export,unset,exit,env. readlineintegration: editing, prompts, and command history (add_history).- Signal-aware interactive loop: handles
SIGINT/SIGQUITin the parent pipeline and restores default handlers in children where appropriate.
This section describes the runtime flow as implemented in the project modules.
- Interactive input: the line is read with
readline()and stored int_info->l(seesrcs/utils/lexer_parser_utils.c). - Tokenization: the lexer (
lexer()insrcs/lexer/lexer_init.c) walks the input string and splits it into a linked list of tokens (t_list), handling:- Separators and metacharacters:
|,<,>,<<,>>, and\n. - Single and double quotes: reading mode is controlled by
quotes(); inside double quotes, variable expansion is performed. - Variable expansion: handled by
expansion()andvar_maker()(e.g.,$VAR,$?,$$,$#), querying the environment table (t_hashtable).
- Separators and metacharacters:
- Execution structure: the parser (
cmd()and helpers insrcs/parser/parser.c) converts the lexed list into a command list (t_token):t_token->param: argument vector (command at index 0).t_token->rdc: redirection vector in pairs (e.g.,">", "out.txt", "<", "in.txt", ...).- Pipelines are represented by chaining via
t_token->next.
- Input → execution: after
lexer(), each “logical line” is executed throughexec_pipeline()(seesrcs/exec/exec_pipeline.c). - External command:
- The shell creates a child process via
fork()(exec_cmd()insrcs/exec/exec.c). - In the child, the executable path is resolved by
get_cmd_path()(seesrcs/exec/path_utils.c) usingPATH. - Execution happens through
execve(path, argv, envp), whereenvpis produced byht_to_matrix()from the environment hashtable. - The parent synchronizes with
waitpid()and converts the status intoexit_code(including signal-related exit codes).
- The shell creates a child process via
- Pipeline:
exec_pipeline()builds the execution chain withpipe()andfork()in a loop (loop_pipeline()insrcs/exec/exec_pipeline.c).- In each child:
- If there is a previous pipe,
prev_fdis connected toSTDIN_FILENOviadup2(). - If there is a next command,
STDOUT_FILENOis connected to the write end of the pipe viadup2().
- If there is a previous pipe,
- The parent closes unused descriptors and waits for all children via
waitpid(-1, ...), preserving the status of the last command in the pipeline.
- In each child:
- Redirections: applied by
handle_redirections()(seesrcs/exec/redirect.c), which:- Opens files via
open()for input (<), output truncate/create (>), or output append (>>). - Redirects input/output via
dup2(fd, STDIN_FILENO/STDOUT_FILENO)and closes the originalfd.
- Opens files via
- Heredoc (
<<):- Pre-processed by
prepare_heredocs()(seesrcs/exec/heredoc.c), which creates temporary files.heredoc_Nand rewrites<<into<pointing to the generated file. - Input is read with
readline("> ")in a loop, stopping at the delimiter or EOF;SIGINTaborts the heredoc and propagates an appropriate exit code.
- Pre-processed by
The shell implements built-ins without relying on external binaries (see srcs/builtins/ and srcs/builtins/builtins.c):
echocdpwdexportunsetexitenv
Important note: built-ins that must modify the parent process state (e.g., cd, export, unset, exit) are executed in the parent when there is no pipeline, detected by is_parent_builtin() (see srcs/exec/path_utils.c). In that case, the shell backs up and restores STDIN/STDOUT using dup()/dup2() while applying redirections.
A reliable minishell requires discipline in memory management and signal handling. The code must build dynamic structures (tokens, argument vectors, environment matrix, and PATH-resolved command paths) without leaking memory across iterations of the interactive loop. For that, the project centralizes cleanup routines (e.g., clean_shell() and clean_token()) and validates behavior with Valgrind (make leaks). At the same time, signal handling must balance interactivity (not “killing” the shell when pressing Ctrl-C) with child execution (restoring SIG_DFL in the child while keeping the parent consistent), producing correct exit codes for interruptions and signal-terminated programs.
- OS: Linux is the primary target environment (POSIX syscalls used as in typical 42 clusters).
- Toolchain:
cc/gcc/clang-compatible compiler andmake. readline: development headers and library must be installed so linking with-lreadlinesucceeds (package names vary by distribution, e.g.libreadline-devon Debian/Ubuntu).valgrind(optional): required only if you intend to runmake leaksfor memory / FD auditing.
git clone https://github.com/claudio1code/minishell42_shellShock
cd minishell42_shellShock
makemake/make all: builds the project and generates theminishellbinarymake clean: removes object files (objs/)make fclean: removes objects and the binary, cleans artifacts, and runsfcleaninlibftmake re: rebuilds from scratchmake leaks: runs the binary under Valgrind (includes suppressions forreadline)
After building, run:
./minishellecho "Hello, world"
pwd
envexport USER42=claudio
echo $USER42
false
echo $?
echo $$echo "line 1" > out.txt
echo "line 2" >> out.txt
cat < out.txtcat /etc/passwd | grep root | wc -lcat << EOF
line A
line B
EOFincludes/: main header (minishell.h) with types (t_info,t_token,t_hashtable) and prototypes.srcs/lexer/: tokenization and reading rules (quotes/expansion).srcs/parser/: builds thet_tokenexecution list and performs syntax checks.srcs/exec/: execution, pipeline, redirections, heredoc,PATHresolution.srcs/builtins/: built-ins and environment utilities.libft/: helper library (includesft_printf,get_next_line, memory/string utilities).
- POSIX.1—Shell Command Language—conceptual shell behavior and redirection semantics.
- GNU Readline—library used for interactive input (
readline(), history). - Manual pages commonly consulted during implementation:
man 3 readline,man 2 fork,man 2 execve,man 2 waitpid,man 2 pipe,man 2 dup2,man 7 signal.
Portions of this README (structure, wording, and technical summaries) were drafted or refined with AI-assisted authoring tools (e.g., Cursor). The C implementation, design decisions in the codebase, and final review remain the responsibility of the project authors (cacesar-, clados-s). If you cite or reuse this repository in an academic setting, disclose tool assistance according to your institution’s policies.
This project is licensed under the MIT License. See the LICENSE file in the repository root for the full text.