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