



[用户名@主机名 当前目录]#












fgets();

从上面的信息,我们可以知道一下几点:
- 这个函数的功能:从一个指定的流拿一个字符串
- 函数的三个参数:第一个参数:存储字符串的数组,第二个参数:获取的最大字符个数,第三个参数:从哪个流获取
从stdin标准输入流(即键盘)中获取内容

接下来我们可以测试一下,我们是否已经成功获取对应字符串

运行结果

显然从上面的结果中,我们发现多出了一个换行符,其实很正常,因为我们在输入命令的时候,最后我们会按下回车,因此,字符串中会多出一个换行,那么我们应该怎么将它清除呢??

strlen(command_line)对应的是数组中最后一个元素的下一个元素,但是我们想要的是最后一个元素的位置,因此应该是将strlen(command_line)-1这个位置的元素改成'\0',也就是将原来是回车的元素改成了'\0'
分割字符串我们需要使用C语言中学习到的一个函数strtok()
我们首先来看一下这个函数的使用方法

显然这个函数有两个参数:
第一个参数是要分隔的字符串
第二个参数是分隔符
这个函数在使用的时候需要注意一些点:
- 第一次使用的时候,第一个参数传要分隔的字符串地址,第二个参数传分隔符,这个分隔符一般情况下使用#define定义成宏,后续比较好修改
- 第二次使用起,第一个参数传NULL,第二个参数传分隔符
这样做的原因是**strtok()函数会自动记录第一次是分隔哪个字符串的**,所以就没有必要再传这个字符串地址了
接下来我们需要对command_line中的内容进行分隔,很明显分隔出来的是一个个的字符串,所以,我们需要一个字符指针数组来存放每一个字符串的地址,这个数组中存放的都是指向字符串的字符指针,也就是字符串的地址

分隔字符串
分隔的时候我们需要设置一个分隔符,这里是以空格作为分隔符


这个是分隔字符串的代码,需要注意的是循环部分的代码:这个是利用到strtok()函数的返回值,如果分隔成功会返回分隔出的字符串的地址,分隔失败会返回空指针,因此分隔到最后的时候会返回空指针,循环自动就结束了
下面我们可以测试一下是否真的成功分隔出来,我们可以打印一下command_arg的内容:

运行结果

成功获取到用户输入的命令并做分隔之后我们就需要创建子进程了

这个代码比较常规,前面已经写过多次

按照我们在命令行输入的习惯,一一般情况下,我们输入的格式都是先输入运行的命令(程序)的名字,然后后面才是各种选项(执行方法),比如:ls -a -l -i,ls就是程序名,后面的依次是选项,因此,分隔出来的第一个字符串就是要运行的程序的名字,后面的内容是告诉这个程序应该怎么运行(运行方法)
运行结果

到目前为止就算做一个基本的shell,可以执行一些基本的指令,比如:
我们要执行ls -a -l -i这样的命令,运行结果如下

当我们使用系统的shell的时候,我们会发现,系统的shell打印出来的一些文件名通常都会带有颜色,而我们的shell打印出来的都是默认没有颜色,那么这是为啥呢??主要是跟我们执行的指令有关系,比如:我们刚刚执行的ls命令是裸的命令,没有经过任何配置的命令,因此打印出来的结果自然是默认不带颜色的,那么我们可以来观察一些系统的ls命令是怎么样的:

这是系统中的ls,我们会发现系统中通过alias给ls设置了一个别名,alias是用于给命令设置别名的,上面的命令意思是:ls --color=auto等价于ls,后面的–color=auto就是给ls显示出来的文件设置一些颜色,接下来我们也可以给我们自己的shell的ls添加这样的功能,操作方法:
源代码:

实验结果:

在上面的shell中,如果外面直接运行起来之后在命令对路径进行更改,我们会发现路径不会发生变化,现象如下:

上面的现象:我们多次通过cd ..将路径更改为上级路径,但是结果不是我们想要的那样,其原因如下:
上面这样的代码执行cd命令的是子进程,子进程可以成功将其路径修改,但是我们知道父子进程间是相互独立的,也就是说子进程的路径发生改变并不会影响父进程的路径,而上面的代码中子进程执行完对应的程序之后就自己退出了,后面父进程继续不断创建新的子进程,那么之前修改路径的子进程并不会影响新创建出来的子进程的路径,新创建出来的子进程的路径是继承其父进程的,由于刚刚的操作父进程的路径并不会受到改变,因此创建出来的子进程的路径继续继承其父进程的路径,那么其执行pwd查看当前进程的路径的时候结果就还是父进程的路径,因此我们看到的结果就是路径并没有发生改变
从上面的分析来看,显然我们需要改变的是父进程的路径,也就是我们自己实现的shell的路径而不是子进程的路径,改变子进程的路径没有任何意义,这个时候就需要使用我们的内建命令了,
内建命令:只能是父进程执行的命令就叫内建命令
这里会使用一个内建命令叫chdir,这个命令的作用就是改变工作路径,注意谁修改路径就让谁去调用

所以上面这个例子,我们应该让父进程去调用chdir


在上面的代码中,我们使用的程序替换函数是execvp,和环境变量没有任何关系,但是我们知道环境变量具有全局属性,因此子进程的环境变量会默认继承父进程的环境变量,在我们当前的系统中,最大的父进程就是bash进程,我们写的shell是bash的子进程,其中的环境变量会全部继承bash进程的环境变量,因此,我们在我们自己的shell中仍然可以使用或者访问bash进程中的所有环境变量
演示:



显然照样能打印出系统中的环境变量
那么如果我们想要在我们自己的shell进程中新增环境变量可以吗?前面我们学过一种方法,就是使用export可以导出环境变量,现在我们可以在我们的shell中尝试一下

显然程序是执行失败的,那么应该怎么做呢?这个时候需要使用到我们的内建命令了,这个时候使用的是putenv()接口

从上面我们可以知道putenv()的作用就是改变或者新增一个环境变量,当环境变量存在的时候即为改变,不存在的时候即为修改

那么如果我们写出这样的代码

实验结果:

程序会执行失败,这是因为我们导出的环境变量的内容是存放在command_line数组中的,而command_line数组中的内容会不断被覆盖清空,所以会将我们的环境变量内容清空掉导致无法导出环境变量,此时我们需要先将环境变量的内容保存在一个新的数组中,然后我们再将这个新数组中的环境变量内容导出给当前shell进程,代码如下:

运行结果:


可见我们成功将我们的自定义环境变量导出给了当前进程