目录
[minishell -- 简易版shell](#minishell -- 简易版shell)
minishell -- 简易版shell
大致思路
- 首先确定,我们的shell是一直在运行的(while(1))
- 每次都会有提示信息打印出来 [xxx@xxx x]
- 输入命令(+选项),其中,我们需要将读入的字符串分开,不然只是没有意义的字符串(然后作为参数传给exec命令)
- 创建子进程执行命令(实际上还是调用系统中的可执行文件)
注意点
ls
ls在我们平常使用的命令行中,是默认添加一个选项的(可以将不同类型的文件用不同的颜色显示出文件名),所以我们记得要把它添加上
cd
- 我们其他指令可以直接让子进程去执行
- 但是!!cd是让shell本身改变当前工作目录的命令,让子进程改变的话将毫无意义
- 因为我们的父进程才是shell !
- 所以一旦执行cd命令,应该在父进程中执行
- 但是不能使用exec系列函数,否则执行完cd后,我们写的shell就退出了(while循环被替换掉了
- 所以直接使用系统调用接口来改变当前工作目录
export
- 如果想要添加环境变量,需要将输入的环境变量手动添加到父进程自己的环境变量中
- 并且,因为我们是使用指针数组存储分开的命令的
- 如果直接将该字符串添加到**environ(也是一个指针数组)**中,也是无法保存下来的
- 因为while每次循环都会将保存输入命令的那个字符串**格式化 --**也就是说,之前输入的环境变量内容也会被清空
- 所以需要我们另外设置一个数组来存储该环境变量,并且保证它不能被清除,即可保存下来输入的环境变量
- 但是,该如何存储呢?
- 如果定位为全局变量,当我们再次定义一个环境变量,就会把前一个替代掉
- 所以我们需要动态开辟空间,最后在shell退出时,才释放资源
代码
cpp#include<stdlib.h> #include<stdio.h> #include<unistd.h> #include<sys/types.h> #include<sys/wait.h> #include<string.h> #define num 1024 #define size 32 #define SEP " " char cmd[num]; char* options[size]; int main(){ extern char** environ; //将环境变量引入进来 while(1){ //打印前缀 printf("root@localhost myshell#"); fflush(stdout); //清空每次输入的命令 memset(cmd,'\0',sizeof cmd); //读入命令 if(fgets(cmd,sizeof cmd,stdin)==NULL){ continue; }//ctrl+c to quit cmd[strlen(cmd)-1]='\0'; //去掉最后读入的\n //分割命令 options[0]=strtok(cmd,SEP); int i=1; if(strcmp(options[0],"ls")==0){ //将ls单独处理 options[i++]="--color=auto"; } while(1){ options[i++]=strtok(NULL,SEP); if(options[i-1]==NULL){ break; } } //export需要提前处理,将环境变量单独存储,并且手动加入到父进程的环境变量中 if(strcmp(options[0],"export")==0){ char* my_env=(char*)malloc(sizeof(char)*sizeof(options[1])); strcpy(my_env,options[1]); putenv(my_env); continue; } // for(i=0;options[i];i++){ //调试用 // printf("%s\n",options[i]); // } //cd需要在父进程进行 if(strcmp(options[0],"cd")==0){ if(options[1]!=NULL){ chdir(options[1]); //change work_path } continue; } //创建子进程 pid_t id=fork(); if(id==0){ printf("im child\n"); execvpe(options[0],options,environ); //执行命令 exit(1); } else{ int status=0; pid_t ret=waitpid(-1,&status,0); if(ret>0){ //success printf("%s:%d\n",options[0],WEXITSTATUS(status)); } else{ //fail printf("fail\n"); } } } return 0; }