• 简易的shell实现


    这篇文章的内容主要是利用进程的创建,等待,终止,替换。这些知识来实现一个自己的简易shell
    在这里插入图片描述

    1. 大致思路

    我们用了这么长的shell,它的大概过程是这样:
    在这里插入图片描述
    其实就是我们在不断的输入一个程序,然后执行它。因为这个shell是不断可以执行程序,所以应该是shell创建一个子进程来完成我们输入的命令。如下图所示:
    在这里插入图片描述
    所以要写一个shell,需要循环以下过程:
    1. 获取命令行
    2. 解析命令行
    3. 建立一个子进程(fork)
    4. 替换子进程(execvp)
    5. 父进程等待子进程退出(wait)

    2. 基本实现

    首先,shell就是一个死循环,当我们输入一个命令结束后,他就会开始下一个命令:
    在这里插入图片描述
    第二步,我们要提示一下用户:
    在这里插入图片描述
    这里我们为什么要这样来刷新,因为我们\n它会换行,我们输入命令一般是在提示符后面输入,而不是在下一行输入。
    在这里插入图片描述
    我们定义一个数组来存放你的字符串,然后把输入的字符串存进数组里:
    在这里插入图片描述
    这里我们一定要把回车清理,因为回车也是一个字符(\n)。我们这里用的fgets函数,可以看一下它的语法:
    在这里插入图片描述
    第三步,我们需要把输入的字符串按空格分割。因为在程序替换时,它是一个一个分开传的,我们可以把分割后的字符串放进一个数组里。
    在这里插入图片描述

    在这里插入图片描述
    = 是故意这么写的,strtok 截取成功,返回字符串其实地址,截取失败,返回NULL。
    这个strtok函数不理解的可以看这个文章:字符函数和字符串函数
    第四步,我们创建一个子进程去进行程序替换:
    在这里插入图片描述
    我们来看一下运行结果:
    在这里插入图片描述
    我们可以看到大致是没有问题的。

    3. 额外拓展

    3.1 让文件带上颜色

    我们平时用的shell,像可执行程序是绿色的,目录是蓝色的。那我们该如何实现呢?
    首先,有一个alias的命令,它的意思是别名。
    在这里插入图片描述
    我们可以看到:myls和ls -a -l执行的一样的,但本质还是ls。那么我们可以看一下系统里的ls。
    在这里插入图片描述
    这个alias的意思是别名。ls本身是不带颜色的,但是加上一些其它选项。所以,我们自己写的时候,子进程程序替换找的是系统的ls,没有加上后面的选项。
    在这里插入图片描述
    所以遇到ls命令时,可以在下标为1的数组里加上这个选项。

    3.2 内建命令

    在这里插入图片描述
    这个现象,我们可以看出我们cd过后,路径没有变化这是为什么。
    在这里插入图片描述
    从这里可以看出,我们用的cd可能不是/usr/bin目录下的。那这个cd是什么呢?
    如果直接exec*执行cd命令,最多只是让子进程进行路径切换,但子进程是一运行就完毕的进程。但是在shell中,我们更希望父进程(shell本身)进行路径切换,因为父进程路径发生变化,后面的子进程也会继承父进程的路径

    如果有些行为,是必须让父进程shell执行的,不想让子进程执行的,绝对不能创建子进程。只能父进程自己实现对应的代码,由shell自己执行的命令,我们称之为内建(bind-in)命令。相当于shell内部的一个函数

    我们先看一下这个函数chdir:
    在这里插入图片描述
    这个系统调用函数意思是:用于改变当前工作目录,其参数为path 目标目录,可以是绝对目录或相对目录。
    在这里插入图片描述
    在这里插入图片描述
    command_arge[1]里面存的就是目标路径。我们看一下运行结果:
    在这里插入图片描述
    这样就完美的解决了。那么有哪些内建命令呢?cd,export,echo这些都是。

    3.3 添加环境变量

    在这里插入图片描述
    我们来看一下这样的运行结果:
    在这里插入图片描述
    所有的程序替换,如果我们不传环境变量,那么它会默认继承父进程的环境变量列表

    在前面,我们使用带e的exec函数时。自己传环境变量,它会覆盖系统的环境变量,那么我们该如果添加式的传环境变量,而不是覆盖式的呢
    在这里插入图片描述
    在这里插入图片描述
    从这个结果可以看到,我们在myshell里export没有办法添加,我们也不能使用environ指针去改变环境变量数组。我们可以使用一个putenv的函数:把字符串加到当前环境中
    在这里插入图片描述
    我们先来演示一下:
    在这里插入图片描述
    在这里插入图片描述
    我们这样来做看能不能成功:
    在这里插入图片描述
    我们可以看到还是没有成功。原因是:
    在这里插入图片描述
    每次循环时,它会重新初始化数组。这样我们写的环境变量的内容下一次就没了,所以我们需要自己保存一下环境变量内容。
    在这里插入图片描述
    这里的env_buffer如果我们不想消失可以用动态开辟的,如果我们想存多个环境变量可以用二维数组。
    在这里插入图片描述
    此时我们再测试一下:
    在这里插入图片描述
    我们看到成功添加了环境变量。

    3.4 重定向的实现

    在这里插入图片描述
    这是我们平时用重定向的方法,左边是一个完整的命令,右边是一个文件,把本来输入到显示屏上,重定向到文件中。
    在这里插入图片描述
    那么在获取用户输入后,它有这几种情况:ls -a -l>log.txt or cat>log.txt or ls -a -l,原来下面写的是分析命令的过程。那么我们就要在这两个之间完成打开文件和重定向的工作。

    那么我们就写一个函数来检查是否重定向:
    在这里插入图片描述
    那么我们就需要从头开始遍历一遍:
    在这里插入图片描述
    如果我们遇到了一个大于符号,那么我们还要进一步判断它下一个位置:
    在这里插入图片描述
    情况分好之后,我们可以定义一些标识符:
    在这里插入图片描述
    以输出重定向为例
    第一步:将遇到的符号改成\0
    在这里插入图片描述
    DROP_SPACE这个意思是:把符号后面的空格去掉。
    在这里插入图片描述
    在这里插入图片描述
    我们把信息记录下来,其它两个也是一样的道理。
    在这里插入图片描述
    在每次循环开始,把文件信息和重定向信息重新设置一下:
    在这里插入图片描述
    到这里,我们重定向信息已经整理好了,现在我们需要完成重定向了。
    在这里插入图片描述
    这里程序替换,会影响曾经子进程打开的文件吗
    不影响,程序替换只会影响该进程的代码和数据,曾经子进程打开的文件是内核数据,程序替换不会影响

    这里在open第三个参数:在创建文件时,设置文件访问权限的初始值。我们创建时,还是设置一下,不然创建出来是随机值。第三个参数是在第二个参数中有O_CREAT时才用作用。若没有,则第三个参数可以忽略

    运行结果如下
    在这里插入图片描述

    总结
    1. 环境变量的数据,保存在进程的上下文中。
    2. 环境变量会被子进程继承下去,所以会有全局属性。
    3. 当我们进行程序替换的时候,当前进程的环境变量非但不会被替换,而且会继承父进程的

    在这里插入图片描述

  • 相关阅读:
    金仓数据库KingbaseES服务器应用参考手册--8. sys_rewind
    nodejs 和 npm 版本对应关系
    【微信自动化】使用c#实现微信自动化
    qml入门
    AVL树的插入
    HikariPool-1 - Exception during pool initialization.
    【Unity】第一人称视角开发
    泊松抠图论文复现
    定积分的应用:几何应用与物理应用
    k8s部署Eureka集群
  • 原文地址:https://blog.csdn.net/qq_52154068/article/details/128072146