前言:颠末过去两章十二篇文章的学习,我们已经知道了进程的基本概念以及进程的控制方法。 本篇内容就是使用过去学习的内容本身写一个功能简单的shell外壳程序, 也就是我们使用的bash命令行。 本篇内容是过去进程知识的集大成者。 我们在这个实战程序中, 将过去学过的进程控制方法——创建、等待、退出、终止,各种概念——PID、优先级、子进程、情况变量、命令行参数等等都会回忆起来, 复习起来。
ps:本节内容很综合, 难度很大。须要学完进程的概念以及进程控制的方法。建议学习前先去复习一遍进程概念相关知识点。
目录
shell是什么
实现过程
交互板块
命令行的外表
办理输入问题:
剖析字符串
平凡命令执行
内建命令
cd命令
ls颜色问题
export
echo
shell是什么
shell是外壳程序, 也就是说bash, shell本质上也是一个进程。 而在执行命令的时候, 对于平凡命令本质上就是本身创建子进程执行的。而对于内建命令, bash命令其实是直接在本身上面调用的一个函数。 我们看到的bash命令行, 其实就是一个输出的字符串。 这个字符串是由用户名 + 主机名 + 当前路径组成的。
我们本身制作的shell主要分为几个板块——用户交互获取命令行、分解命令行参数、内建命令执行、平凡命令执行
实现过程
交互板块
命令行的外表
用户名, 主机名,当前路径都生存在情况变量中。
那么, 我们就可以封装函数, 获取三个情况变量——一个是用户名、一个是主机名、一个是当前路径
主函数部分我们这里只写一下输出, 先将命令行的外表打印出来:
上图的三个黄框框是博主定义的宏, LEFT是打印"[", RIGHT是打印"]"。
然后对于平凡用户最后一个字符一样平常都是$符号, 那么我们就定义一个LABLE的宏, 让这个宏替换$。如下图:
此时完备的代码如下:
此时运行结果就能打印一个属于我们本身的命令行:
办理输入问题:
但是不能输入, 接下来我们就办理输入问题。我们可以定义一个字符串,这个字符串的长度是1024。
在主函数这里添加一个scanf:
要留意此时黄框框这里没有\n, 不然输入参数会多打印一个换行。 此时的运行结果如下, 可以输入了:
但是此时只能输入, 这些输入的字符不能全部生存到commendline数组中。 因为scanf碰到空格会终止读取。 所以就用到了另一个输入函数——fgets , 如下图:
fgets中, 第一个参数是要拷贝到的起始地址, 第二个参数是要拷贝的字节数, 第三个是从哪个文件读取。 我们是从表现器读取, 也就是stdin。
上图的s用来接收fgets的返回值, fgets的返回值是读取乐成后的地址, 假如读取不乐成就是返回NULL。
下图是运行结果:
上面我们打印之所以多了一个换行, 是因为我们在输入的时候回车符被读取进去了。 也就是相称于printf那边有一个我们本身加的换行, 然后commendline最后一个字符也是一个换行。 commendline内里的换行是我们不想要的, 请问我们怎么去掉呢?——既然回车符是最后一个字符, 那么我们就把最后一个字符酿成\0。如下图:
下面是打印结果:
如今, 我们将我们上面写的命令行的外表, 命令行的获取这些与用户交互的操作独自封装成一个函数——Interact。
以上, 我们就实现了与用户的交互,获取命令行。 大体就是这样, 但是后续可能随着我们其他板块的增长, 修改内里的部分代码。 但不会影响大思路。
剖析字符串
在正式剖析字符串之前我们先把多次扣问的问题办理。 因为我们的shell命令行是一直存在的, 不应该是使用一次后就退出。 这里我们要给代码套上一层循环。 ——即在我们要实现的逻辑:交互、剖析、执行的外面套上一层死循环。这样就能让逻辑循环起来了。
运行情况就是如下,程序正常情况下已经不会退出了。(ctrl + C可以退出)
接下来正式对命令行进行剖析:
下面是剖析的原理:
strtok函数如下:
这里我们先定义一个分隔符的宏:
然后创建一个数组, 用来存储切割好的字符串。
然后我们就实现截取字串, 将字串放到argv中, 放进去后就i++, 比及截取不了了就返回一个空串。 但是strtok第一次截取的时候可以传参commendline, 假如之后还是截取这个字符串, 那么就传NULL,我们把剖析字符串也单独封装成一个函数, 返回值是一个argc, 也就是截取出来的字符串的个数。 如下图:
写好之后, 我们就在主函数上放上测试代码:
如下就是分解后的结果:
平凡命令执行
然后就是执行命令,当id == 0的时候执行。
这个时候, 假如execvpe加载乐成, 就执行加载的程序假如加载失败, 就直接退出。并且退出码EXIT_CODE, 这个是我们本身定义的宏。
我们这里先定义一个lastcode获取状态信息:
下面是执行平凡命令的代码, environ是获取情况变量, 然后创建子进程, 假如id小于0的时候, 分析子进程创建失败, 那么直接返回。 假如id == 0, 分析是子进程,就加载程序。 我们的argv是数组, 所以带v, 但是没有绝对路径, 只有文件名, 所以要有默认搜索路径, 也就是加p。 另外还要有情况变量, 加上e。
假如是id > 0就分析是父进程, 父进程要等待子进程, 也就是下图的代码等待子进程。
然后, 我们运行我们的程序就可以执行一样平常的命令了。
内建命令
cd命令
但是, 这里有一个问题, 就是我们使用cd的时候, 我们会发现, 我们的工作目录不发生变化。这个是因为cd是一个内建命令!!!cd不是由bash的子进程加载得来的。就如同下图:
为什么会这样呢? 因为我们使用的fork创建的子进程。 子进程数据的修改不会改变父进程。 所以我们使用cd命令的时候, 固然加载了cd命令, 但是子进程使用cd命令, 子进程工作路径修改, 不会影响父进程。
那么正常使用cd命令, 就不能创建子进程, 我们可以使用chdir。 如今是内建命令的处置惩罚:
对于这些内建命令, 我们的办理方式是对于这些内建命令一个一个地做特别处置惩罚。 起首, 为了方便维护, 我们同样将内建命令的执行封装成一个函数:
函数要带参数argc, 我们的代码都是在这个函数中完成。
内建函数创建后, 我们的大体逻辑就搭建好了, 如今来看一下主函数的逻辑——先交互, 再剖析, 再执行:
回到cd上来,起首chdir可以修改路径。 chdir可以修改当前的工作路径。但是我们也要获取当前路径用来修改PWD情况变量。
这里我们可以使用getcwd, 先创建一个pwd字符数组, 用来生存当前工作路径:
然后将我们原先定义的pwd修改成下图:、
既然获取当前路径的代码变了, 我们上面写的代码中, 有些地方也要改, 起首是交互函数的修改, 下图的红框框是getpwd的使用, 黄框框是工作路径的获取:
然后是内建函数的修L
此时, 我们的cd就能跑了:
ls颜色问题
处置惩罚颜色问题, 须要在最后加一个--color选项。 假如命令是ls, 那么就要处置惩罚一下, 也就是再最后加一个--color选项。 最好的是在命令行剖析的函数里进行处置惩罚。 但是在内里处置惩罚会有argc的返回值问题。 所以为了方便, 博主这里放在了内建命令执行的函数里, 因为内建命令执行函数在平凡命令执行函数之前, 并且博主的内建命令也有argc, 方便控制。
当我们再运行时, 就能看到ls的颜色了:
写到这里的时候, 博主发现了一个问题, 就是我们可以给内建命令一个返回值, 只要返回真, 分析执行了内建命令, 假就没有执行。 这样就能防止又执行了内建, 又执行了平凡。
export
如今看一下新建情况变量export:
假如我们不做特别处置惩罚, export创建情况变量, 是创建不出来的。因为我们直接使用export, 那就是创建子进程, 然后加载到子进程帮我们执行,然而子进程不会影响父进程。 所以就没有效处。如下图就是创建不出的例子:
所以, 这里就须要将export也当作内建命令特别处置惩罚——使用putenv在当进步程导入情况变量。 但是由于putenv导入情况变量只是修改情况变量的指针指向传给的参数指向的空间。也就是相称于一个浅拷贝。 假如我们直接给putenv传argv[1], 那么情况变量的指针指向putenv指向字符串, 当这个字符串被覆盖时情况变量就变了!!所以我们要先malloc一块新空间。 再将数据拷贝到这个空间。 让情况变量指向这块创建的malloc空间。
当进步程导入情况变量。 那么就能使用我们本身的shell导入情况变量了。
echo
对于echo也要做一下特别处置惩罚。 因为一样平常情况下echo会打印正常, 但是对于情况变量来说, 它就会直接打印情况变量
处置惩罚方式就是做一个特别判定, 假如argv[1]的第一个字符时$那么就按照情况变量打印:
如下图红框框处是做一下特别判定, 防止发生段错误。
这样就能把情况变量打出来:
但是还不可, 因为echo可能打印$, 也就是打印最后一次退出码。
那么就要再进行一次特别处置惩罚:
lastcode内里生存了退出码, 当执行了一次echo $?后要把lastcode置为0
当我们当进行登录的时候, 我们的系统就是要启动一个shell进程。 我们shell本身的情况变量表是从哪里来的??——是在当前用户的家目录下, 有一个叫做bash_profile 大概 bashrc的文件。 这内里就有各种各样的文件。
当用户登录的时候, shell会读取用户目录下的.bash文件, 内里生存了导入情况变量的方式!!!
假如我们想要本身导入这种默认的情况变量, 那么我们就要和标准的shell一样, 创建一个情况变量表。 然后本身创建一个情况变量的设置文件。将这些情况变量读入情况变量表当中!!!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |