Quantcast
Viewing all articles
Browse latest Browse all 9596

用Python从头开发一个自己的Shell(下)

python从头开发一个自己的Shell(下)

一点号编程派昨天

平常工作中经常用到 shell 吧?好不好奇 shell 的具体执行方式?今天推送的这两篇文章,将利用 Python 实现一些简单的 shell 功能。

在第一部分中,我们已经实现了主要的 shell 循环,切分了命令输入,并通过fork和exec执行了输入命令。在本文中,我们将解决剩下的问题。第一个问题是cd test_dir2并未改变当前目录。第二个问题是我们仍不能优雅地退出 shell。cd test_dir2并未改变当前目录,这种说法在某种意义上说既是对的又是错误的。正确是因为执行命令之后,仍在相同的目录。然而,目录确实改变了,不过是在子进程中改变的。还记得我们fork了一个子进程,然后执行命令,这个命令并不会在父进程中执行,导致仅仅改变了子进程的目录,而没有改变父进程的目录。

之后,子进程终止,父进程仍保持原先的目录。

因此,这类命令必须为 shell 本身内置,并得在 shell 进程中执行,而不是通过fork。cd

我们先从cd命令开始。

首先建立一个builtins目录,来存放内建命令。

yosh_project |-- yosh |-- builtins | |-- __init__.py | |-- cd.py |-- __init__.py |-- shell.pycd.py中,通过使用系统调用os.chdir实现自己的cdimportos fromyosh.constantsimport* 注意到在内建函数中会返回 shell 的运行状态,所以,我们把常量写进yosh/constants.py 中供项目使用。yosh_project |-- yosh |-- builtins | |-- __init__.py | |-- cd.py |-- __init__.py |-- constants.py |-- shell.pySHELL_STATUS_STOP =0SHELL_STATUS_RUN =1现在,内建的cd命令已经就绪。接下来修改shell.py来处理内建函数。... # Import constantsfromyosh.constantsimport* # Hash map to store built-in function name and reference as key and valuebuilt_in_cmds = {} deftokenize(string): returnshlex.split(string) defexecute(cmd_tokens): # Extract command name and arguments from tokens cmd_name = cmd_tokens[0] cmd_args = cmd_tokens[1:] # If the command is a built-in command, invoke its function with arguments ifcmd_nameinbuilt_in_cmds: returnbuilt_in_cmds[cmd_name](cmd_args) ...使用 Python 字典built_in_cmds,作为存储内建函数的哈希表。在execute函数中,将命令名及参数取出,如果命令名在哈希表中,就调用对应的函数。我们马上就可以使用内建的cd函数了,最后一步是将cd函数加到built_in_cmds中。... # Import all built-in function referencesfromyosh.builtinsimport* ... # Register a built-in function to built-in command hash mapdefregister_command(name, func): built_in_cmds[name] = func 定义register_command函数来向内建命令哈希表中添加内建函数,然后定义init函数,并注册内建cd函数。注意register_command("cd", cd)这一行代码。第一个参数是命令名,第二个参数是函数的引用。为了使第二个参数cd指向yosh/builtins/cd.py中的cd函数,我们需要在yosh/builtins/__init__.py因此,在yosh/shell.py中,当从yosh.builtins中import *时,就得到了cd函数的引用。代码已经准备就绪,来尝试在yosh同级目录下运行python -m yosh.shell。现在,我们的cd命令应该能够正确改变 shell 目录,同时哪些非内建命令也同样有效。退出

下面是最后一步:优雅地退出。

,这样 shell 循环将会中断,shell 程序也将会结束并退出。像cd一样,如果在子进程中fork并执行exit命令,对父进程不会有影响。因此,exit函数必须为内建函数。在builtins文件夹下新建exit.pyyosh_project |-- yosh |-- builtins | |-- __init__.py | |-- cd.py | |-- exit.py |-- __init__.py |-- constants.py |-- shell.pyexit.py中定义了exitfromyosh.constantsimport* 然后,在 中导入exitshell.py中的init函数中注册exit函数:... # Register all built-in commands heredefinit: register_command("cd", cd) register_command("exit", exit) ...

大功告成!

尝试运行,现在输入exit就可以优雅地退出程序了。我希望你像我一样,享受创造yosh(属于自己的shell)的过程,但是yosh现在还是粗糙,我没有处理可能导致 shell 中断的特殊情况,也有很多内建功能没有覆盖到。一些非内建函数也可以作为内建函数实现,以提高性能(避免创建新进程的时间开销),还有很多功能没有实现(参考常见功能和特殊功能)

我在github上提供了源码,欢迎 fork 并把玩。

现在,轮到你来创造属于自己的 shell 了。

祝编码愉快!

Python 翻译组是EarlGrey@编程派发起成立的一个专注于 Python 技术内容翻译的小组,目前已有近 30 名 Python 技术爱好者加入。

翻译组出品的内容(包括教程、文档、书籍、视频)将在编程派微信公众号首发,欢迎各位 Python 爱好者推荐相关线索。

推荐线索,可直接在编程派微信公众号推文下留言即可。

欢迎转发至朋友圈。如无特殊注明,本公号所发文章均为原创或编译,如需转载,请联系「编程派」获得授权。

扫码关注编程派


Viewing all articles
Browse latest Browse all 9596

Trending Articles