A minimal Unix-style shell implementation in C supporting pipes, I/O redirection, background execution, and signal handling.
The shell consists of three main components:
- Parser (
src/parser.c): Tokenizes command lines and builds pipeline structures - Shell Core (
src/shell.c): Implements REPL loop, process execution, and I/O redirection - Header (
include/shell.h): Defines data structures and function interfaces
The shell uses standard Unix process control primitives (fork, execvp, waitpid) to execute commands and manage pipelines.
βββββββββββββββββββ
β Shell Start β
ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Setup Signal β
β Handlers β
ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Print Prompt β
β "shell> " β
ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Read Command β
β Line (fgets) β
ββββββββββ¬βββββββββ
β
ββββββββββββββ΄βββββββββββββ
β β
βΌ βΌ
βββββββββββββββββ ββββββββββββββββ
β EOF (Ctrl+D)β β Read Error β
βββββββββ¬ββββββββ ββββββββ¬ββββββββ
β β
β β
βββββββββββββ¬ββββββββββββ
β
βΌ
βββββββββββββββββββ
β Remove Newline β
ββββββββββ¬βββββββββ
β
βΌ
βββββββββββββββββββ
β Empty Line? β
ββββββββββ¬βββββββββ
β
ββββββββββββββ΄βββββββββββββ
β β
YES NO
β β
β βΌ
β βββββββββββββββββββ
β β Built-in cmd? β
β β (e.g., exit) β
β ββββββββββ¬βββββββββ
β β
β ββββββββββββββ΄βββββββββββββ
β β β
β YES NO
β β β
β β βΌ
β β βββββββββββββββββββ
β β β Parse Command β
β β β (parse_command)β
β β ββββββββββ¬βββββββββ
β β β
β β ββββββββββββββ΄βββββββββββββ
β β β β
β β Parse Error Success
β β β β
β β β βΌ
β β β βββββββββββββββββββ
β β β β Execute β
β β β β Pipeline β
β β β β(execute_pipeline)β
β β β ββββββββββ¬βββββββββ
β β β β
β β β βΌ
β β β βββββββββββββββββββ
β β β β Create Pipes β
β β β β (N-1 pipes for β
β β β β N commands) β
β β β ββββββββββ¬βββββββββ
β β β β
β β β βΌ
β β β βββββββββββββββββββ
β β β β For Each Cmd: β
β β β β - fork() β
β β β β - dup2() pipes β
β β β β - setup I/O β
β β β β - execvp() β
β β β ββββββββββ¬βββββββββ
β β β β
β β β βΌ
β β β βββββββββββββββββββ
β β β β Background? β
β β β ββββββββββ¬βββββββββ
β β β β
β β β ββββββββββββββ΄βββββββββββββ
β β β β β
β β β YES NO
β β β β β
β β β β βΌ
β β β β βββββββββββββββββββ
β β β β β waitpid() for β
β β β β β all children β
β β β β ββββββββββ¬βββββββββ
β β β β β
β β β β βΌ
β β β β βββββββββββββββββββ
β β β β β Get Exit β
β β β β β Status β
β β β β ββββββββββ¬βββββββββ
β β β β β
β β β β β
β β β ββββββββββββββ¬βββββββββββββ
β β β β
β β β β
β β ββββββββββββββ¬βββββββββββββ
β β β
β β β
β ββββββββββββββ¬βββββββββββββ
β β
β β
βββββββββββββββ¬ββββββββββββ
β
β
βΌ
βββββββββββββββββββ
β Check SIGINT β
β (Ctrl+C) β
ββββββββββ¬βββββββββ
β
β
βΌ
βββββββββββββββββββ
β Loop Back to β
β Print Prompt β
βββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Stack Segment β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β shell_main() stack frame β β
β β - line[MAX_LINE_LEN] (4096 bytes) β β
β β - pipeline_t structure β β
β β - Local variables β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β execute_pipeline() stack frame β β
β β - pids[] array (pointer to heap) β β
β β - pipe_fds[][] array (pointer to heap) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Heap Segment β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β pipeline_t.commands[] β β
β β βββββββββββββββ βββββββββββββββ βββββββββββββ β β
β β β command_t β β command_t β β command_t β β β
β β β - argv[] β β - argv[] β β - argv[] β β β
β β β - input_f β β - input_f β β - input_f β β β
β β β - output_f β β - output_f β β - output_fβ β β
β β βββββββββββββββ βββββββββββββββ βββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Token strings (from parser) β β
β β - argv[0], argv[1], ... (char*) β β
β β - input_file, output_file (char*) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Process ID arrays β β
β β - pids[] (pid_t*) β β
β β - pipe_fds[][] (int[2]*) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Kernel Space (File Descriptors) β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Process File Descriptor Table β β
β β 0: stdin β terminal β β
β β 1: stdout β terminal (or pipe/file) β β
β β 2: stderr β terminal β β
β β 3: pipe[0] β read end β β
β β 4: pipe[1] β write end β β
β β ... β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The parser tokenizes the input line and identifies:
- Command arguments (space-separated tokens)
- Pipe operators (
|) - Redirection operators (
<,>,>>) - Background execution (
&)
The parser builds a pipeline_t structure containing an array of command_t structures.
For a pipeline like cmd1 | cmd2 | cmd3:
- Create Pipes: Allocate N-1 pipes for N commands
- Fork Processes: Create child process for each command
- Setup I/O: Use
dup2()to redirect stdin/stdout through pipes - Execute: Call
execvp()to replace child process image - Wait: Parent waits for all children (unless background)
- Input (
<): Opens file and duplicates toSTDIN_FILENO - Output (
>): Opens file (truncate) and duplicates toSTDOUT_FILENO - Append (
>>): Opens file (append) and duplicates toSTDOUT_FILENO
Redirection takes precedence over pipes when both are specified.
- SIGINT (Ctrl+C): Handler sends signal to foreground process group
- Parent shell ignores interrupt and continues REPL loop
- Background processes are not affected by Ctrl+C
Commands ending with & execute in background:
- Parent does not wait for completion
- Process ID is printed
- Shell immediately returns to prompt
# Compile
gcc -Wall -Wextra -std=c11 -Iinclude src/shell.c src/parser.c -o shell
# Run
./shell
# Example commands
shell> ls -la
shell> echo "hello" | grep "h"
shell> ls > output.txt
shell> cat < input.txt
shell> sleep 5 &- Process control (
fork,execvp,waitpid) - Pipeline execution (
|) - Input redirection (
<) - Output redirection (
>,>>) - Background execution (
&) - Signal handling (SIGINT/Ctrl+C)
- Command parsing and tokenization