Building A Shell

My first "real" and systems-level project

It was quite a big leap from my previous endeavors. So far, I only had dead-simple static frontend pages, some CLI tools in Python, and a bunch of Linux commands under my belt.

And now I had to make the thing that runs these CLI apps, and that too in C? It had to be fast, of course. I did not even know where to start, since everything I had built so far had some kind of reference guide.

I wandered around a lot of websites, but they were all too comprehensive for me to wrap my head around, until I stumbled upon a blog post by Stephen Brennan [1].

It taught me about the core components of a shell, and more importantly, how to break a large project into modules, namely: lexer [2], parser [3], and executor.

I researched more about these concepts and also how a command is executed at the CPU level, which is through system calls [4]:

A process is basically a program being executed.

fork(): Clones the calling process to create an exact duplicate child process [5].
exec(): Replaces the current process image with a completely new program [6].
wait(): Forces a parent process to pause execution until its child process finishes [7].

There are many more, but these three will suffice for a minimal implementation. Now what do you do with these calls? Create built-in commands like cd, exit, and help, and execute other programs whose binaries are installed on your computer.

A high-level overview:
Take commands and arguments.
Separate them into tokens.
Parse the tokenized input.
Perform system calls for your custom commands or call the built-in binaries.
Handle errors gracefully and glue everything together.
And there you have it: a minimal implementation of a UNIX shell!

P.S. I tried to simplify my understanding as much as I could. I intentionally excluded code snippets so you can research and decide on an implementation yourself. If you still want to see the code, you can find it on my GitHub [8].

Note: This is a very basic and minimal implementation. It lacks a lot of features like conditionals, output redirection, piping, tab completion, globbing, and a ton of other things. But the point is that you can add these as you go! The guide above is more than enough for a working project.

I hope this blog helped. Now go build a shell for yourself!

~ Parth Naik

Citations:

  1. Stephen Brennan: Write a Shell in C
  2. Lexical analysis
  3. Parsing
  4. System call
  5. fork(2)
  6. exec(3)
  7. wait(2)
  8. psh on GitHub