Writing a Shell in C
2 min read
Writing a Simple Shell in C — A Quick Reflection
I recently followed Stephen Brennan’s classic tutorial on building a tiny Unix shell in C (https://brennan.io/2015/01/16/write-a-shell-in-c/), and honestly, it was one of the best ways to understand what bash and zsh actually do behind the scenes.
It turns out a shell is way simpler than it feels. At its core, it just repeats this cycle:
-
Read what the user types
-
Parse the input into a command and arguments
-
Run that command
-
Wait for it to finish
-
Loop forever
Once I saw this broken down, the terminal felt much less “magical.”
How It Works Under the Hood
Reading Input
You grab a line from the user — either with getline() or by manually reading characters.
This becomes the raw command string the shell will interpret.
Splitting Into Tokens
The input gets chopped into words like "ls", "-la", and "/usr/bin" using something like strtok().
This becomes the argv array passed to programs.
Built-ins vs External Commands
This part helped me understand real shells a lot better:
-
Commands like
ls,cat, andgrepare external programs. -
Commands like
cd,exit, andhelphave to run inside the shell process, so the shell implements these manually.
That’s why typing cd doesn’t launch a new program — it changes the shell’s working directory directly.
Running Programs with fork/exec
This is the coolest section.
The shell:
-
forks to make a child process
-
The child calls execvp(), replacing itself with the requested program
-
The parent calls waitpid() to pause until the program finishes
Seeing this in action makes you appreciate how Unix multitasking really works.
The Whole Shell in One Loop
Everything ends up inside a simple infinite loop:
-
show a prompt
-
read input
-
parse it
-
run it
-
repeat
And when that loop works, and you type ls into your own shell and it actually prints your directory contents — that’s a very cool moment.
Final Thoughts
Writing a shell was surprisingly small in code but huge in insight.
It teaches you:
-
why real shells separate built-ins from external binaries
-
how processes are spawned and replaced
-
what actually happens when you hit Enter in a terminal
If you're comfortable with C and curious about how bash/zsh operate internally, this is the perfect hands-on project.
Again, here’s the tutorial: