当前位置: 首页 > news >正文

shell的模拟实现 ─── linux第16课

在shell的命令行中输入命令,会有两种执行命令的途径

  1.         shell自己执行
  2.         shell创建子进程(fork ,exit ,waitpid,exec) ,子进程去执行

shell自己执行的命令是自建命令(bulit command)

子进程执行的是非自建命令

第一版只能维护命令行参数表+创建子进程, 执行非内建命令

        我们先创建了命令行提示符 ,获取了命令行的内容,维护了命令行参数表,创建了子进程进行命令的执行

​
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<stdlib.h>
  4 #include<cstring>
  5 #include<string>
  6 #include<unistd.h>
  7 #include<sys/types.h>
  8 #include<sys/wait.h>
  9 
 10 using namespace std;
 11 const int charsize =1024;
 12 const int gargvnum =64;
 13 
 14 //全局的
 15 char* gargv[gargvnum];
 16 int gargc;
 17 
 18 
 19 int lastcode = 0;
 20 
 21 string GetUsrName()
 22 {
 23     string name =getenv("USER");
 24     return name.empty()?  "None" : name;
 25 }
 26 string GetHostName()
 27 {
 28     string name =getenv("HOSTNAME");
 29     return name.empty()?  "None" : name;
 30 }
 31 string GetPwd()
 32 {
 33     string name =getenv("PWD");
 34     return name.empty()?  "None" : name;
 35 }
 36 string MakeCommandLine()
 37 {  //[root@hcss-ecs-1f3a lesson17]#  
 38     char CommandLine[charsize];
 39     snprintf(CommandLine ,charsize,"[%s@%s %s]# ",\
 40         GetUsrName().c_str(),GetHostName().c_str(),GetPwd().c_str());
 41     return CommandLine;
 42 }
 43 void PrintCommandLine()//1.打印命令行提示符
 44 {
 45     printf("%s",MakeCommandLine().c_str());
 46     fflush(stdout);
 47 }
 48 
 49 
 50 bool GetCommand(char Command_buff[] ,int size)//2.获取命令
 51 {
 52     //将命令输出到字符数组中
 53     //ls -a -l -n
 54     char*result =fgets(Command_buff,size,stdin);
 55     if(!result)
 56     {
 57         return false;
 58     }
 59     Command_buff[strlen(Command_buff)-1]= 0;//fgets会将回车(\n)也输入
 60     if(strlen(Command_buff) == 0) return false;//strlen遇0(\0)会停下来
 61     return true;
 62 }
 63 
 64  void ParseCommand(char Command_buff[] ,int size)//3.分析命令
 65 {
 66     memset(gargv ,0,sizeof(gargv));
 67     gargc=0;
 68     const char* SEP =" ";
 69     gargv[gargc++] = strtok(Command_buff ,SEP);//strtok 会在字符串中查找分隔符,并将分隔符替换为 \0,从而将字符串分割成多个
 70     while((bool)(gargv[gargc++] = strtok(nullptr,SEP)));//strtok后续使用要用nullptr代替元字符串
 71     gargc--;
 72 }
 73 
 74 void debug()
 75 {
 76     printf("argc: %d\n", gargc);
 77     for(int i = 0; gargv[i]; i++)
 78     {
 79         printf("argv[%d]: %s\n", i, gargv[i]);
 80     }
 81 }
 85 bool ExecuteCommand()//4.执行命令
 86 {
 87     pid_t id =fork();
 88     if(id ==0)
 89     {//子进程
 90         int ret =execvp(gargv[0],gargv);
 91         if(ret ==-1) cout<<"子进程出错\n"<<endl;
 92         exit(1);
 93     }
 94     int status =0;
 95     pid_t rid =waitpid(id,&status ,0);
 96     if(rid >0)
 97     {
 98         if(WIFEXITED(status))
 99         {
100            lastcode=WEXITSTATUS(status);
101         }
102         else lastcode =100;
103         return true;
104     }
105     return false;
106 
107 }
108 
109 int main()
110 {
111     char Command_buff[charsize];
112     while(true)
113     {
114         PrintCommandLine();//1.打印命令行提示符
115 
116         if(!GetCommand(Command_buff,charsize))//2.获取命令
117         {
118             continue;
119         }
120         ParseCommand(Command_buff ,charsize);//3.分析命令
121         //debug();
122         ExecuteCommand();//4.执行命令
123     }
124     return 0;
125 }

​

第一版的执行结果:

  • 第一版在执行cd ..时,是改变了子进程的cwd,子进程执行完又退了,无法影响下面的进程,cd.. 不能让子进程执行.
  • 所以cd .. 或cd / 是自建命令 ,需要shell自己执行,以便可以影响到下面的进程(例如在/ 目录下创建文件)

 第二版能维护命令行参数表+执行cd命令 ,判断了是否是自建命令(mysell自己执行自建命令,可以对环境变量发生改变),子进程执行其他命令.

在执行创建子进程前判断,命令是否是内建命令(是否是cd 命令),是否要创建子进程.

getpwd不再是从环境变量中拿,从pcb中拿(因为pcb中是实时的),拿完更新环境变量(命令行提示符每次运行都会刷新,借此来维护cd 后的环境变量).

    #include<iostream>
  2 #include<cstdio>
  3 #include<stdlib.h>
  4 #include<cstring>
  5 #include<string>
  6 #include<unistd.h>
  7 #include<sys/types.h>
  8 #include<sys/wait.h>
  9 
 10 using namespace std;
 11 const int charsize =1024;
 12 const int gargvnum =64;
 14 //全局的
 15 char* gargv[gargvnum];
 16 int gargc;
 17 
 19 
 20 
 21 //全局的当前shell的工作路径(定义到全局不会被销毁)
 22 char pwd[charsize];
 23 char pwdenv[charsize];
 24 
 25 int lastcode = 0;
 26 
 27 string GetUsrName()
 28 {
 29     string name =getenv("USER");
 30     return name.empty()?  "None" : name;
 31 }
 32 string GetHostName()
 33 {
 34     string name =getenv("HOSTNAME");
 35     return name.empty()?  "None" : name;
 36 }
    string GetPwd()
 38 {
 39     //string name =getenv("PWD");
 40     //return name.empty()?  "None" : name;
 41 
 42 
 43     //从pcb中直接拿pwd
 44     if(nullptr ==  getcwd(pwd,sizeof(pwd))) return "None";
 45     //拿到后还需要更新环境变量中的pwd
 46     snprintf(pwdenv,sizeof(pwdenv),"PWD=%s",pwd);
 47     putenv(pwdenv);
 48     return pwd;
 49 
 50 
 51 }
 52 string MakeCommandLine()
 53 {  //[root@hcss-ecs-1f3a lesson17]#  
 54     char CommandLine[charsize];
 55     snprintf(CommandLine ,charsize,"[%s@%s %s]# ",\
 56         GetUsrName().c_str(),GetHostName().c_str(),GetPwd().c_str());
 57     return CommandLine;
 58 }
 59 void PrintCommandLine()//1.打印命令行提示符
 60 {
 61     printf("%s",MakeCommandLine().c_str());
 62     fflush(stdout);
 63 }
 64 
 65 
 66 bool GetCommand(char Command_buff[] ,int size)//2.获取命令
 67 {
 68     //将命令输出到字符数组中
 69     //ls -a -l -n
 70     char*result =fgets(Command_buff,size,stdin);
 71     if(!result)
 72     {
 73         return false;
 74     }
 75     Command_buff[strlen(Command_buff)-1]= 0;//fgets会将回车(\n)也输入
 76     if(strlen(Command_buff) == 0) return false;//strlen遇0(\0)会停下来
 77     return true;
 78 }

 80  void ParseCommand(char Command_buff[] ,int size)//3.分析命令
 81 {
 82     memset(gargv ,0,sizeof(gargv));
 83     gargc=0;
 84     const char* SEP =" ";
 85     gargv[gargc++] = strtok(Command_buff ,SEP);//strtok 会在字符串中查找分隔符,并将分隔符替换为 \0,从而将字符串分割成多个
 86     while((bool)(gargv[gargc++] = strtok(nullptr,SEP)));//strtok后续使用要用nullptr代替元字符串
 87     gargc--;
 88 }
 89 
 90 void debug()
 91 {
 92     printf("argc: %d\n", gargc);
 93     for(int i = 0; gargv[i]; i++)
 94     {
 95         printf("argv[%d]: %s\n", i, gargv[i]);
 96     }
 97 }
 98 
 99 
100 
101 bool ExecuteCommand()//4.执行命令
102 {
103     pid_t id =fork();
104     if(id ==0)
105     {//子进程
106         int ret =execvp(gargv[0],gargv);
107         if(ret ==-1) cout<<"子进程出错\n"<<endl;
108         exit(1);
109     }
110     int status =0;
111     pid_t rid =waitpid(id,&status ,0);
112     if(rid >0)
113     {
114         if(WIFEXITED(status))
115         {
116            lastcode=WEXITSTATUS(status);
117         }
118         else lastcode =100;
119         return true;
120     }
         return false;
122 
123 }
124 
125 //内建命令的执行(调用函数,改变状态)
126 bool CheckandExecBuiltCommand()
127 {   //使用穷举法找内建命令
128     if(0 == strcmp(gargv[0],"cd"))
129     {
130         if(gargc == 2)
131         {
132             chdir(gargv[1]);
133         }
134         return true;
135     }
136     return false;
137 
138 }
139 
140 int main()
141 {
142     char Command_buff[charsize];
143     while(true)
144     {
145         PrintCommandLine();//1.打印命令行提示符
146 
147         if(!GetCommand(Command_buff,charsize))//2.获取命令
148         {
149             continue;
150         }
151         ParseCommand(Command_buff ,charsize);//3.分析命令
152         //debug();
153         //判断是否是内建命令,shell自己执行
154         if(CheckandExecBuiltCommand())//是内建命令并执行
155         {
156             continue;
157         }
158         //不是内建命令,创建子进程执行
159         ExecuteCommand();//4.执行命令
160     }
161     return 0;
162 }

第二版执行结果:

第三版 模拟真实shell从系统文件中获取环境变量,维护命令行参数表+维护环境变量表(execvpe)

  • myshell前面的两版都是从系统shell中获取的环境变量表
  • 实际上,系统shell开启时,是从系统文件中获取环境变量表,但是这个过程涉及shell脚本(比较难搞,意义不大),
  • 所以我们用将系统shell的环境变量表手动拷贝到myshell中的过程来模拟系统shell开启时,是从系统文件中获取环境变量表

  • 要让myshell执行的子进程的环境变量与myshell一致(不与系统shell一致)使用execvpe
  • 系统的shell维护了两张表(命令行参数表+环境变量表)

第三版拷贝了环境变量表,维护了环境变量表(exxcvpe),增加了内置命令

第三版运行结果:

相关文章:

  • 邮件发送器:使用 Python 构建带 GUI 的邮件自动发送工具
  • 什么是OFD文件?2025年我推荐新版OFD阅读器和PDF阅读器,双合一
  • 【算法方法总结·五】链表操作的一些技巧和注意事项
  • linux学习(五)(服务器审查,正常运行时间负载,身份验证日志,正在运行的服务,评估可用内存)
  • 【实战ES】实战 Elasticsearch:快速上手与深度实践-5.1.1热点分片识别与均衡策略
  • 闭包函数是什么?
  • 【算法day5】最长回文子串——中心拓展方法
  • mysql安装(演示为mac安装流程)
  • 自动控制原理【知识点总结、复习笔记】
  • 论文阅读-秦汉时期北方边疆组织的空间互动模式与直道的定位(中国)
  • MySQL-----SELECT语句-查询
  • JVM组成面试题及原理
  • 《云原生监控体系构建实录:从Prometheus到Grafana的观测革命》
  • Linux——.sh 多个阶段的串行和并行执行程序
  • 向量内积(点乘)和外积(叉乘)
  • Linux一键安装zsh终端美化插件
  • MySql自动安装脚本
  • JavaEE进阶(2) Spring Web MVC: Session 和 Cookie
  • BroadcastReceiver的应用
  • 算法 之 堆
  • 做网站的好公司/石家庄seo优化公司
  • 南京网站设计公司哪儿济南兴田德润怎么联系/免费搜索引擎入口
  • 独立网站开发/电商网
  • 谁有永久免费的服务器/开鲁网站seo不用下载
  • axure怎么做网站首页/什么是优化
  • 网站建设培训龙岗/信息流广告