用Python从头开发一个自己的Shell(上)
toyiye 2024-09-12 20:57 3 浏览 0 评论
编程派微信号:codingpy
平常工作中经常用到 shell 吧?好不好奇 shell 的具体执行方式?今天推送的这两篇文章,将利用 Python 实现一些简单的 shell 功能。
本文原作者为 Supasate Choochaisri ,由 PythonTG 翻译组的 Justin 翻译,校对为 EarlGrey。译者简介:Justin,python工程师一枚,对go、docker感兴趣,还在成长ing。
我很好奇 shell(比如 bash、cash等)内部的工作原理,所以我用 Python 实现了 yosh(Your Own Shell)来满足自己的好奇心。我在本文中阐释的概念同样适用于其他语言。
Step 0: 项目结构
在这个项目中,我使用了下面的结构:
yosh_project
是项目根文件夹(你也可以用 yosh
来命名)。
yosh
是包文件夹,__init__.py
会让包与文件夹同名(如果你不写 python,可以忽略这点)。
shell.py
是主要的 shell 文件。
Step 1: Shell 循环
启动 shell 时,它会立刻展示命令提示符并等待输入。在接收到命令并执行完毕(细节会在后面讲到)后,shell 会再次回到等待循环,准备接收下一条命令。
在 shell.py
中,我们通过主函数调用 shell_loop()
函数,来启动循环。代码如下:
然后在 shell_loop()
函数中,使用 status
标志来表示循环是否应该继续。在循环开始时,shell 将立即显示命令提示符,并等待输入。
接下来,对输入的命令进行切分(tokenize),并执行(稍后会实现 tokenize
和 execute
函数)。
现在,shell_loop()
函数应该如下:
上述就是整个的 shell 循环。如果通过 python shell.py
启动 shell,会立即显示命令提示符。但是如果我们输入命令并回车,就会抛出错误,因为我们还没有定义 tokenize
函数。
要退出 shell,可以使用 ctrl-c
,稍后我会介绍如何优雅地终止 shell。
Step 2: 切分(Tokenization)
用户在 shell 中键入命令并按下回车时,输入的命令是一条长长的字符串,其中包含了命令名以及参数。因此,我们必须将其切分(将字符串拆分成多个 token)。
字符串切分乍一看很简单。我们可能会使用 cmd.split()
根据空格来分割输入的命令。对于形如 ls -a my_folder
的命令是奏效的,因为 cmd.split()
会将其拆分为一个列表 — ['ls', '-a', 'my_folder’]
,这样我们使用起来就比较容易了。
但是,某些情况下,某些参数会带有单引号或者双引号,比如 echo "Hello World”
或者 echo 'Hello World’
。如果我们使用 cmd.split()
, 将会得到一个包含三个 token 的列表 — ['echo', '"Hello', 'World”’]
,而不是包含两个 token 的列表 — ['echo', 'Hello World’]
。
幸运地是,Python 提供了一个叫做 shlex
的库,可以很好地帮助我们分词(注:我们也可以使用正则表达式,但这不是本文的重点)。
然后,我们将这些 token 传给执行进程。
Step 3: 执行
这是 shell 核心,而且也是有趣的部分。shell 执行 mkdir test_dir
时会发生什么呢?(注:mkdir
是一个程序,传递 test_dir
参数执行后会创建名为 test_dir
的目录)。
这一步中涉及的第一个函数是 execvp
。在解释 execvp
的功能之前,让我们先来实际使用一下。
尝试再次运行 shell 并输入命令 mkdir test_dir
,然后回车。
现在的问题是,我们敲击回车后,shell 并未等待下一条命令,而是终止了。但是目录成功被创建。
所以,execvp
到底做了什么呢?
execvp
是系统调用 exec
的一个变种。第一个参数是程序名。v
表示第二个参数是程序参数列表(可变的参数个数)。p
表示 PATH
环境将用于搜索给定的程序名。在我们之前的尝试中,mkdir
程序就是基于 PATH
环境变量。
(exec
还有其他变种,比如 execv
、execvpe
、execl
、execlp
、execlpe
等,你可以google一下获取更多信息。)
exec
会将当前调用进程的内存,替换为一个即将执行的进程。在我们的示例中,shell 进程的内存被 mkdir
程序替换。然后 mkdir
变为主进程,并创建 test_dir
目录,最后进程终止。
这里最主要的一点是,shell 进程已经被 mkdir
进程取代。这也是为什么 shell 会终止而不是等待下一条命令。
所以,我们需要另外一个系统调用 fork
来解决这个问题。
fork
会分配新的内存,并将现有进程拷贝到新的进程,我们称这个新进程为子进程,调用进程为父进程。之后,执行过 exec
的程序将会取代子进程。因此,作为父进程的 shell 就不用进行内存替换了。
下面是修改后的代码:
当父进程调用 os.fork()
时,你可以认为所有的源码都被拷贝到子进程。这时父子进程的代码相同,而且并行执行。
运行的代码若属于子进程,pid
为 0;若属于父进程,pid
为子进程的 id。
如果 os.execvp
在子进程中被调用,子进程的所有源码将被调用程序的源码替换掉。但是,父进程的代码没有改变。
父进程等待子进程终止后,会返回继续 shell 循环的状态。
运行
现在你可以尝试运行我们自己打造的 shell ,并键入 mkdir test_dir2
。shell应该会正常运转,而且 shell 进程会继续等待你输入下一条命令。尝试 ls
命令就会看到已创建的目录。
但是,还是存在一些问题。
第一点,输入 cd test_dir2
,然后再输入 ls
,应该会进入到 test_dir2
这个空目录中,但是目录并没有切换到 test_dir2
。
第二点,我们仍不能优雅地退出 shell。
上述问题将会在 Part2 中解决。
Python 翻译组是EarlGrey@编程派发起成立的一个专注于 Python 技术内容翻译的小组,目前已有近 30 名 Python 技术爱好者加入。
翻译组出品的内容(包括教程、文档、书籍、视频)将在编程派微信公众号首发,欢迎各位 Python 爱好者推荐相关线索。
推荐线索,可直接在编程派微信公众号推文下留言即可。
相关推荐
- 为何越来越多的编程语言使用JSON(为什么编程)
-
JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...
- 何时在数据库中使用 JSON(数据库用json格式存储)
-
在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...
- MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)
-
前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...
- JSON对象花样进阶(json格式对象)
-
一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...
- 深入理解 JSON 和 Form-data(json和formdata提交区别)
-
在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...
- JSON 语法(json 语法 priority)
-
JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...
- JSON语法详解(json的语法规则)
-
JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...
- MySQL JSON数据类型操作(mysql的json)
-
概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...
- JSON的数据模式(json数据格式示例)
-
像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...
- 前端学习——JSON格式详解(后端json格式)
-
JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...
- 什么是 JSON:详解 JSON 及其优势(什么叫json)
-
现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...
- PostgreSQL JSON 类型:处理结构化数据
-
PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...
- JavaScript:JSON、三种包装类(javascript 包)
-
JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...
- Python数据分析 只要1分钟 教你玩转JSON 全程干货
-
Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...
- 比较一下JSON与XML两种数据格式?(json和xml哪个好)
-
JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- r语言矩阵 (127)
- browsererror (114)
- exportexcel (119)
- cv2.bitwise_not (137)
- dump命令 (128)
- es6concat (126)
- heapify (127)
- java.security.egd (130)
- javax.annotation (117)
- jsstringsplit (117)
- js数字 (115)
- maven编译 (132)
- mysqlleft (128)
- nodejsbuffer (149)
- org.apache.commons.httpclient (126)
- org.jsoup (141)
- org.springframework.web (128)
- robotframework-ride (115)
- setnocounton (141)
- socket.gethostbyname (122)
- sqlmid (121)
- time.strptime (133)
- vscode格式化 (125)
- win32con (129)
- window.localstorage (126)