Home  >  Article  >  Backend Development  >  How to create your own Shell with Python (Part 2)

How to create your own Shell with Python (Part 2)

巴扎黑
巴扎黑Original
2017-03-18 11:54:371140browse

In the previous article, we have created a shell main loop, split the command input, and executed the command through fork and exec. In this part, we will address the remaining issues. First of all, the cd test_dir2 command cannot modify our current directory. Second, we still can't exit gracefully from the shell.

Step 4: Built-in command

"cd test_dir2 cannot modify our current directory" This sentence is correct, but it is also wrong in a sense. It's correct in the sense that we are still in the same directory after executing the command. However, the directory has actually been modified, but it has been modified in the child process.

Remember that we forked a child process and then executed the command. The process of executing the command did not occur on the parent process. The result is that we only change the current directory of the child process, not the directory of the parent process.

Then the child process exits, and the parent process continues to run in the intact directory.

Therefore, such commands related to the shell itself must be built-in commands. It must be executed in the shell process and not in forking.

cd

Let’s start with the cd command.

We first create a builtins directory. Every built-in command will be placed in this directory.

yosh_project
|-- yosh
   |-- builtins
   |   |-- __init__.py
   |   |-- cd.py
   |-- __init__.py
   |-- shell.py

In cd.py, we implement our own cd command by using the system call os.chdir.

import os
from yosh.constants import *
def cd(args):
    os.chdir(args[0])
    return SHELL_STATUS_RUN

Note that we will return the running status of the shell from the built-in function. So, to be able to continue using constants in the project, we moved them to yosh/constants.py.

yosh_project
|-- yosh
   |-- builtins
   |   |-- __init__.py
   |   |-- cd.py
   |-- __init__.py
   |-- constants.py
   |-- shell.py

In constants.py, we put all the state constants here.

SHELL_STATUS_STOP = 0
SHELL_STATUS_RUN = 1

Now, our built-in cd is ready. Let's modify shell.py to handle these built-in functions.

...
### 导入常量
from yosh.constants import *
### 使用哈希映射来存储内建的函数名及其引用
built_in_cmds = {}
def tokenize(string):
    return shlex.split(string)
def execute(cmd_tokens):
    ### 从元组中分拆命令名称与参数
    cmd_name = cmd_tokens[0]
    cmd_args = cmd_tokens[1:]
    ### 如果该命令是一个内建命令,使用参数调用该函数
    if cmd_name in built_in_cmds:
        return built_in_cmds[cmd_name](cmd_args)
    ...

We use a python dictionary variable built_in_cmds as a hash map hash map to store our built-in functions. We extract the name and parameters of the command in the execute function. If the command is in our hash map, the corresponding built-in function is called.

(Tip: built_in_cmds[cmd_name] returns a function reference that can be called directly with arguments.)

We are almost ready to use the built-in cd function. The final step is to add the cd function to the built_in_cmds mapping.

...
### 导入所有内建函数引用
from yosh.builtins import *
...
### 注册内建函数到内建命令的哈希映射中
def register_command(name, func):
    built_in_cmds[name] = func
### 在此注册所有的内建命令
def init():
    register_command("cd", cd)
def main():
    ###在开始主循环之前初始化 shell
    init()
    shell_loop()

We define the register_command function to add a built-in function to our built-in command hash map. Next, we define the init function and register the built-in cd function here.

Pay attention to this line register_command("cd", cd). The first parameter is the name of the command. The second parameter is a function reference. In order to allow the second parameter cd to refer to the cd function reference in yosh/builtins/cd.py, we must put the following line of code in yosh/builtins/__init__.py file.

from yosh.builtins.cd import *

So, in yosh/shell.py, when we import * from yosh.builtins, we can get the yosh.builtins Imported cd function reference.

We have the code ready. Let us try to run our shell as a module in the same directory as yosh, python -m yosh.shell.

Now, the cd command can correctly modify our shell directory, and non-built-in commands can still work. very good!

exit

The final piece is finally here: exit gracefully.

We need a function that can modify the shell status to SHELL_STATUS_STOP. In this way, the shell loop can end naturally and the shell will reach the end and exit.

Same as cd, if we fork and execute the exit function in the child process, it will have no effect on the parent process. Therefore, the exit function needs to be a shell built-in function.

Let’s start here: Create a new file called exit.py in the builtins directory.

yosh_project
|-- yosh
   |-- builtins
   |   |-- __init__.py
   |   |-- cd.py
   |   |-- exit.py
   |-- __init__.py
   |-- constants.py
   |-- shell.py

exit.py defines a exit function, which only returns a status that can exit the main loop.

from yosh.constants import *
def exit(args):
    return SHELL_STATUS_STOP

Then, we import the exit function reference located in the yosh/builtins/__init__.py file.

from yosh.builtins.cd import *
from yosh.builtins.exit import *

Finally, we register the exit command in the init() function in shell.py.

...
### 在此注册所有的内建命令
def init():
    register_command("cd", cd)
    register_command("exit", exit)
...

That’s it!

Try to execute python -m yosh.shell. Now you can type exit to exit the program gracefully.

Final Thoughts

I hope you enjoy creating yosh (your own ## as much as I did) #shell) process. But my version of yosh is still in its early stages. I didn't deal with some corner cases that would crash the shell. There are a lot of built-in commands that I didn't cover. To improve performance, some non-builtin commands can also be implemented as built-in commands (to avoid new process creation time). At the same time, a large number of features have not yet been implemented.

I have provided the source code at https://github.com/supasate/yosh. Please feel free to fork and try it out.

Now it's time to create a shell that is truly your own.

Happy Coding!

The above is the detailed content of How to create your own Shell with Python (Part 2). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn