shell的命令行设计
其中包含用户名,系统,当前所处的文件夹
这些信息我们可以从环境变量里面筛选得到
使用getenv获取,在封装为函数,获取我们想要的信息
变成函数提取出去
void PrintCommmandLine() { printf("[%s@%s %s]#", GetUSER(), GetHOSTNAME(), GetPWD()); fflush(stdout); }现在我们暂且让路径是这个样子
获取用户输入
,我们接下来再看
ls -l -a首先我们要将输入的命令,进行解析按照空变成->ls -l -a. strtok函数在这里很有用
要选择什么来进行输入呢??
scanf??->scanf是以空格作为分割符,在这里显然不太合适
这里我推荐使用fgets
char *fgets(char *str, int n, FILE *stream);剔除换行
但是我们发现这里会多了一些不必要的换行.因为我们每一次输入的结尾一定会按了一下enter也就是换行所以我们这里要将command_line的末尾用\0来代替
if( fgets(command_line,sizeof(command_line),stdin)==NULL) continue; command_line[strlen(command_line)-1]='\0';在变成函数
命令行解析
strtok 详解
实现子串一变多,根据自己设定的分割符
char *strtok(char *str, const char *delim);str:待分割的字符串delim:分隔符集合
- 返回值
- 分割成功:返回当前分割出来的子串首地址
- 分割完毕:返回
NULL
那怎么获得全部呢
strtok
- 第一次调用:第一个参数传入待分割字符串数组
- 找到第一个分隔符:将分隔符改成
\0,返回第一段字符串首地址 - 找不到分隔符:直接返回整个字符串首地址
- 找到第一个分隔符:将分隔符改成
- 后续循环调用:第一个参数必须传
NULL- 从上次停下的位置继续向后查找分隔符,找到就返回下一段地址
- 再也找不到任何片段时,返回 NULL
strtok在命令解析的使用
执行任务
我们不让父进程执行,要让子进程来执行,一但让父进程来执行进程就会退出我们的命令行操作
根据我们之前讲命令行是放在数组里面的,这里我们要使用的程序替换函数使用带v的比较好
void ExecuteCommand() { pid_t id=fork(); if(id<0) { //创建子进程失败 perror("fork"); } else if(id==0) { //child extern char** environ; execvpe(margv[0],margv,environ); perror("execvp fail"); exit(0); } else{ //parent int status=0; pid_t rid=waitpid(id,&status,0); if(rid>0) { printf("wait success\n"); } else { perror("waitpid"); } } }我们通过上面的实践发现可以完成很多的命令操作了
存在问题
但是像是一些内建命令还是没有办法实现
还有之前的遗留问题,
这里应该只有myshell
解决相应的问题
解决cd的问题
首先cd命令应该要由父进程执行,如果让子进程执行,他结束后不会和父进程同步更新
解决工作目录打印的问题
查看env
cd回到家目录
再次查看时
现象说明
- 启动 myshell:环境变量
PWD=/xxx/Myshell - 执行
cd→chdir()成功,内核目录切换到家目录 - 但是代码没有更新环境变量 PWD,内存里依然保留旧值
- 执行外部命令
pwd:- 系统
pwd命令优先读取环境变量 PWD,打印旧路径;
- 系统
此时我们发现环境变量不会自主更新,所以我们要自己来更新pwd,通过cd时来更新
解决部分echo内建命令
定义全局变量,将退出码记录出来,要在父进程执行或者子进程执行时即使更新退出码
对于子进程,我们让父进程waitpid来获得子进程的退出码
父进程自己来来更新退出码
获取环境变量