Write A Shell In Action (1)
Why should we write a shell? Well, I think the most important reason is that it is fun. Learning how to write a shell will improve the system programming experience for the UNIX environment. Well, as the Feynman said:
What I cannot create, I do not understand
The Minimal Setup
In this tutorial, we will use the c++ as the language to write the shell. So at we should use cmake to construct the build environment. If you are not familiar with cmake, don’t worry about it. It’s easy to understand. For convenience, I hope you can just use the following commands to initialize the repository.
1 | mkdir miniShell && cd miniShell |
At this time, you will find the miniShell.cpp under the root tree, the functionality of the miniShell.cpp is simple enough. Because this is tutorial, we just simplify the program. We assume miniShell would have only two modes like any shell:
- Interactive
- Batch mode
The idea is actually the same. However, at now. We do not implement any functionality.
1 |
|
If you are interested in the CMakeLists.txt. You could just provide its content to the ChatGPT, it will give you a wonderful explanation. At now, you could build this program:
1 | mkdir build && cd build && cmake .. && cmake --build . |
Under the build directory, you will find the miniShell executable.
The Parser
We have already set up a minimal development environment, now we need to parse the shell script. Actually, writing a parser could be another theme. So in this tutorial, we will just use native way to parse the shell script.
So we should first make sure the functionality of the shell. I don’t think it’s a good idea to support the condition or loop control. It’s not the point. We just want to learn the system programming. Below is the spec:
The operations for variables.
1
2
3
4
5variable=5
echo $variable # 5
echo ${variable} # 5
echo '${variable}' # ${variable}
echo "${variable}" # 5Command substitution
1
2variable=`echo 5` # variable=5
variable=$(echo 5) # variable=5Redirection
1
2
3echo '5' > /tmp/txt
echo '55' >> /tmp/txt
cat < /tmp/txtPipe
1
command1 | command2 | command3 | command4
Job control
1
2sleep 100 &
bg %1
As you can see, actually we could simplify the question. We first split the script line by line. For each line, we split the line by |. And we will generate the following classes for further process:
Variable: handle thea=3.Command: handle the<command> <argument1> <argument2>.PipeCommand: handle theCommand1 | Command2 | Command3.
I will not dive into this part, because it would be annoying and far away from the theme of this tutorial. Here, I give a snippet:
1 | class Command : public CommandBase { |
From the above snippet, all we need to do is define the execute function. For each line, we will call the execute function. You could use the following command to pull the latest code:
1 | git pull upstream add-parser |
Below is the code hierarchy, if you are interested about the detail, you could see the parser directory.
1 | . |
Look at the miniShell.cpp:
1 |
|
Due to the nice abstraction from cpp, the file stream and standard input stream are all the istream. So we define a start command to abstract the process. The most important function you could see is the execute function.
A Simple Start
Now we try a simple command /usr/bin/ls, we will write the method Command::execute. We will use fork system call to create a child process and waits for its action which would use exec system call for /usr/bin/ls. If you do not understand these concepts, you could read the man page of the fork or just read Advanced Programming in the UNIX Environment chapter 8.
1 | void Command::execute() { |
However, the above code is clear. What we do is simply pass the command and arguments to the execv system call. Like the following figure illustrates, when we type /usr/bin/ls, the result is OK. However, when we type ls, there is no result. This is because we need to search the $PATH environment. We will improve our code for part 2.

As usual, you could use the following command to get the code:
1 | git pull upstream simple-ls |
Author: shejialuo
Link: https://luolibrary.com/2023/12/24/Write-A-Shell-In-Action-1/
License: CC BY-NC 4.0