打印环境变量,把当前子进程所有环境变量打印出来
环境变量也是一张表(指针数组以null结尾,最后条件不满足就退出了 )
用子进程调用
结论1
当我们进行程序替换的时候 ,子进程对应的环境变量(子进程的环境变量是直接从父进程来的)
那么父进程的环境变量是从哪里来的呢? bash父亲,myprocess子,mytest孙子
给bash导入一个变量
执行mytest后果然继承了bash的变量
putenv
把变量到入myprocess(注意pitenv是char*类型的)
子进程mytest也可以看到,最终子进程的环境变量会越来越多
结论2.
环境变量被子进程继承下去是一种默认行为,为什么不受程序替换的影响 原理?
程序替换,只替换新程序的代码和数据,环境变量不会被替换
命令行参数在栈上
通过地址空间可以让子进程继承父进程的环境变量数据 (子进程也可以看到)
子进赋值他的pcb和进程地址空间
environ
也可以打印环境变量
execle
a:直接传环境变量
第一个绝对路径,第二个参数以null结尾代表就三个,最后一个传环境变量environ(他是char**的)而execle点个参数是指针数组,数组传参发生降为就是char**的
b:我们想传递自己的环境变了怎么办
我们不传给他系统默认的环境变量,而是自己写一个传给他(自定义了一个myenv表)
这就拿到了我们自己传的环境变量表
结论:
选了带e的 我们可以直接构造环境变量表,给子进程传递,并且带e的不是在原有的环境变量基础上新增你自定义的变量表,而是覆盖上去,只有你的环境变量表(全新的环境变量表)
c:当我们想在原有的基础上新增呢?
当我们在父进程创建变量后,直接传自己的环境变量就会把新创建的变量传递给子进程(新增)
结论
程序替换可以将命令行参数,和环境变量通过自己的参数,传递给被替换程序的main函数中
execvpe和execle
左边都是号手册,只有execve是2号手册
上面个最终都会转化成execve(他是真正的系统调用)
都会转成execve的参数(文件名,命令行参数,环境变量)为什么呢?
主要是为了满足各种调用使用场景(选择合适的)
自己实现shell
env查看环境变量中的USER,hostname,pwd我们用getenv来获取(头文件stdlib.h)
如果存在return,没有返回none
成功创建自己的命令行提示符,下一步把光标卡在这里,并不是自动调到下一行
定一个命令行usercommand[num],把输入的内凡在userconmand里面(这样就可以停住了)
要把\n去掉
ls -a -l回显测试 但是只显示ls 因为scanf遇到 空格 就结束了,所以我们不用scanf
直接用fgets/gets
获取成功了就是字符串的起始地址,失败就是none
stream(stdin)读到缓冲区(char*,int size)
c语言默认会打开三个输入输出流 stdin键盘 stdout显示器 stderr显示器(他们的类型FILE*)
可以用sizeof直接求数组大小么?一般是sizeof(usercommand)是可以的
获取成功返回r失败返回1,然后printf回显打印出来,但是多了一个空行!
去掉prif中\n虽然没有换行但是重新另起一个命令行了,因为最终你输入ls-a-l 还有一个\n
怎么去掉最后一个\n呢?他是最后一个字符
把\n的位置改成\0,但是会不会越界呢? strlen(userconmand)-1<0? 不会!
因为敲完命令最少有一个\n
完成,获得了命令行输出
封装上面代码
1 打印提示符&&获取用户命令字符串获取成功
2 分割字符串
分割成 ls,-a,-l 首先定义一个指针数组(以null结尾),怎么切割呢?每个子串后面加\0(独立起来)
我们用c的话用分割函数(1变多)
size定义成64把argv初始化为空
strtok
把字符串按照 指定的 分隔符,打散成子串
截取成功返回起始地址,失败返回null
每次按照分隔符提取一个子串,然后后面的传null就会继续分割后序的子串
当前子串在usercommand【num】里面 ,然后后面的传null就会继续分割后序的子串
截取成功返回起始地址,失败返回null,最后一次截取正好为null让argv以null结尾
此时argv里面存的就是 ls,-a ,-l ,null
截取测试
截取成功
封装字符串切割函数
至此切割完成
3 执行分割命令
当前是一个进程,用fork函数
让子进程去执行 exec command (调用程序替换接口) 创建子进程(不让子进程往后走用exit(1))
父进程做的是等待子进程 此时引入 waitpid(等待的id,退出码,退出方式)
然后用子进程调用程序替换函数接口,我们选择(做更少的工作,不用导环境变量)
我们选择execvp
argv[0]就是ls,我们想怎么执行程序(在命令行怎么传)就用 argv
但是我们的命令行只能跑一次
用while循环套起来就可以一直执行了
当我们一直敲回车r就会返回1
把获取到的字符串的长度返回(可能为0空串可能大于0 )
获取返回的长度,如果n<=0说明获取失败(-1),或者是空串 那么就continue跳出下面的语句
执行命令封装
id<o创建子进程失败返回-1
cd..路径并没有不变,为什么呢?
是子进程执行的cd..,当我们自己执行命令时候是bash环境中进行的
shell的内建命令
(就是bash自己执行的,类似于自己内部的一个函数)
所以有一批命令不能交给子进程执行,只能由父进程来执行
所有执行命令前要知道他是否是 内建命令(所以我们要检查)
只有带color我们执行的命令才会带颜色(按照颜色方式执行)
4.检查是否是内建命令
n=doBuildfin(argv)
1表示是内建命令 0表示不是内建命令
首先确认是否 是内建命令用strcmp(跟内建命令比较)
比如跟cd比较 是cd并且执行cd 并返回1
是内建命令就continue不继续往下走了 循环看下一个指令
如果不是就返回0 继续往下走
如果argv[1]参数没有设置(cd后加没有命令(没东西))走到cd parh一定不为空
来到cd函数,我们要切换哪一个路径呢? 怎么样让自己对应的当前进程,去执行一个接口,能够把我们当前的路径变了
chdir
谁调用chdir就把谁当前的工作路径更改,所以我们改argv[1]
更改后 chdir谁就改成了谁的路径
但是我们=命令行的路径没有变,为什么呢?
因为我们吗每次执行时候获取的环境变量都没有变
方法二
sprintf
把格式化的字符串format往str里面打印
当前环境变量叫做pwd
把对应的格式化的内容“PWD=%s”导入到cwd里面去,同时用putenv导入到当前环境变量,谁掉putenv就导入到谁中,此时路径确实变了,此时env找不到pwd了(因为路径已经变了)
因为环境变量的存储空间不能在临时空间里面去存,必须永久有效
函数调用时有,putenv导入后只是把cwd【】的地址导进去了(表里面)但是地址没有
所以改成全局变量 此时env可以看到pwd了
getcwd
把当前进程所在的绝对路径放在缓冲区里面,返回值是获取成功时获取的内容
获取当前绝对路径后再通过sprintf(把格式化的字符串format往str里面打印)放在cwd里面
此时cwd拿的就是chdir之后的 当前进程的 绝对工作路径(经过getcwd),这次不怕临时空间了,因为我们把整个emp格式化到cwd里面了,再putenv 重新导入
让子进程导入export,第一次env可以看到
但是再次env就看不到6666666666666666了
因为argv[1]是由 *argv【size】指向的,而该指针数组指向的是usercommand【num】里面子串
而usercommand【num】每次都再输入指令是都会覆盖写入,下次usercommand【num】里面的缓冲区已经被改变了,所以将环境变量不能用临时缓冲器缓冲(比如刚才全局变量cwd)
所以我们定义一个大的全局变量表,导入进去
导环境变量的动作 用strcpy
此时我们就存到我们定义的enval里面,然后导出就导出我们的enval(enval不受其他用户影响)
如果export后是空就退出ruturn 1
提取最近一个进程退出时的退出码(echo $?/$PATH)
argv[1]应该指向$+1就指向PATH,后面的是?
既如果argv[1]是?就是查看退出码,是PATH就是查看环境变量(用getenv获取)
效果代码