TinyShell
A minimal POSIX shell with job control, pipes, redirections, and background process management.
Overview
TinyShell is a minimal but real POSIX shell written in C. It supports the core features
you’d expect from bash — command execution, pipes, I/O redirection, background jobs,
and basic job control — implemented from scratch using fork, exec, pipe, and waitpid.
Features
- Pipelines —
cmd1 | cmd2 | cmd3with correct fd wiring - Redirection —
>,<,>>,2>usingdup2 - Background jobs —
&suffix,jobsbuiltin,fg/bgcontrol - Builtins —
cd,exit,echo,jobs,fg,bg - Signal handling —
SIGCHLDfor zombie reaping,SIGINT/SIGTSTPforwarding
The Pipeline Challenge
The trickiest part is wiring pipes correctly in a multi-stage pipeline.
Each process needs the read end of the previous pipe and the write end of the next.
Getting the close() calls right to avoid deadlocks took several iterations.
// For n commands, create n-1 pipes
int pipes[n-1][2];
for (int i = 0; i < n-1; i++) pipe(pipes[i]);
for (int i = 0; i < n; i++) {
pid_t pid = fork();
if (pid == 0) {
if (i > 0) dup2(pipes[i-1][0], STDIN_FILENO);
if (i < n-1) dup2(pipes[i][1], STDOUT_FILENO);
// close all pipe fds before exec
execvp(cmds[i].argv[0], cmds[i].argv);
}
}
What I Learned
- How a shell is fundamentally just a
fork/exec/waitloop - Why
SIGCHLDhandling is necessary to avoid zombie processes - The subtlety of process groups and sessions for proper job control
- How
dup2replaces file descriptors to wire up pipes invisibly to child programs