【Linux】编写简易shell && 深度理解命令行解释器 && 环境变量 && 内建命令

🌻个人主页路飞雪吖~

🌠专栏:Linux


目录

主体:

一、命令行提示符

二、获取用户命令

三、分析命令

四、执行命令

五、内建命令

[🌠 小贴士:](#🌠 小贴士:)

六、环境变量

七、总结

八、完整代码


如若对你有帮助,记得关注、收藏、点赞哦~ 您的支持是我最大的动力🌹🌹🌹🌹!!!

若有误,望各位,在评论区留言或者私信我 指点迷津!!!谢谢 ヾ(≧▽≦*)o \( •̀ ω •́ )/

主体:

cpp 复制代码
 int main()
 {
     while(true)
     {
         PrintCommandLine();   // 1. 命令行提示符
         
         GetCommandLine();    // 2. 获取用户命令
         
         AnalysisCommandLine(); // 3. 分析命令
         
         ExecuteCommand(); // 4. 执行命令
     }
     return 0;
 }

一、命令行提示符

命令行提示符一般由:用户名+主机名+当前工作路径 组成。我们如何获取呢?这就不得不提到我们之前提到过的 环境变量表了,我们知道每个子进程都会继承父进程的环境变量表,所以我们可以从环境变量表来获取用户名、主机名、当前工作路径。

cpp 复制代码
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <unistd.h>
  6 #include <sys/types.h>
  7 #include <wait.h>
  8                                
  9 using namespace std;                  
 10  
 11 int basesize = 1024;              
 12                                                                                            
 13 string GetUserName()                                                          
 14 {                               
 15     string username = getenv("USER");                
 16     return username.empty() ? "None" : username;                            
 17 }                                            
 18                             
 19 string GetHostName()                         
 20 {                                          
 21     string hostname = getenv("HOSTNAME");  
 22     return hostname.empty() ? "None" : hostname;  
 23 }                           
 24                             
 25 string GetPWD()             
 26 {                           
 27     string pwd = getenv("PWD");                   
 28     return pwd.empty() ? "None" : pwd;  
 29 }                           
 30                                                     
 31 string MakeCommandLine()    
 32 {                                               
 33     char command_line[basesize];  
 34     snprintf(command_line, basesize, "[%s@%s %s]# ",\  
 35             GetUserName().c_str(), GetHostName().c_str(), GetPWD().c_str());  
 36     return command_line;    
 37                             
 38 }                     
 39 void PrintCommandLine()  // 1. 命令行提示符  
 40 {    
 41     printf("%s",MakeCommandLine().c_str());  
 42     fflush(stdout);  
 43 }                     
 44    
 45 int main()       
 46 {
 47     while(true)
 48     {
 49          PrintCommandLine();  // 1. 命令行提示符
 50          printf("\n");
 51          sleep(1);
 52          //GetCommandLine();    // 2. 获取用户命令
 53 
 54          //ParseCommandLine();  // 3. 分析命令
 55 
 56          //ExecuteCommand();    // 4. 执行命令
 57     }
 58     return 0;
 59 }

二、获取用户命令

• 从键盘当中获取出来的字符串,放到command_buffer缓冲区里面

• 将用户输入的命令行,当作完整的字符串

• "ls -a -l -n" 包括空格

fgets(获取字符串, 指定大小, 从特定的文件流当中获取)

• 获取失败返回null,获取成功返回获取成功的字符串的起始地址

cpp 复制代码
 46 bool GetCommandLine(char command_buffer[], int size)    // 2. 获取用户命令
 47 {                                     
 48     char *result = fgets(command_buffer, size, stdin);
 49     if(!result)                       
 50     {                                 
 51         return false;                 
 52     }                                 
 53     command_buffer[strlen(command_buffer)-1] = 0; // 把最后输入的回车字符改为0,即纯净版输入                                    
 54     if(strlen(command_buffer) == 0) return false;
 55     return true;                      
 56 }                                     
 57                                       
 58 int main()                            
 59 {                                     
 60     char command_buffer[basesize];    
 61     while(true)                       
 62     {                                 
 63          PrintCommandLine();  // 1. 命令行提示符
 64                                       
 65          if(!GetCommandLine(command_buffer, basesize))    // 2. 获取用户命令
 66          { 
                // 依次获取每个子字符串                           
 67             continue; // 获取到空格继续                
 68          }                            
 69          printf("%s\n",command_buffer);
 70                                       
 71          //ParseCommandLine();  // 3. 分析命令                                             
 72 
 73          //ExecuteCommand();    // 4. 执行命令
 74     }
 75     return 0;
 76 }

三、分析命令

解析出来的命令,放到自己构建的全局的环境变量表当中

• strtok(一个字符串分多个,分隔符) 返回值:按照该分隔符从源字符串里切出来的第一个区域

• strtok(str, " "); 如果切割有效的字符串,返回具体的字符串的第一个地址,

• strtok(nullptr, " "); 否则,返回nullptr,即切到最后没有了,自动返回nullptr

cpp 复制代码
      const int argvnum = 64;
      const int envnum = 64;

      char *gargv[argvnum];// 全局的环境变量表
      char gargc = 0; // 记录环境变量的个数

   65 void ParseCommandLine(char command_buffer[], int len)  // 3. 分析命令
   66 {
   67     (void)len;
   68     memset(gargv, 0, sizeof(gargv)); // 初始化/清空环境变量表里面的内容
   69     gargc = 0;
   70 
   71     const char *sep = " ";
   72 
   73     gargv[gargc++] = strtok(command_buffer, sep); // 放到全局的环境列表里面
   74 
   75     // 循环放入环境变量表中
   76     while((bool)(gargv[gargc++] = strtok(nullptr, sep)));
   77     gargc--;
   78 }
   79 
   80 void debug()
   81 {
   82     printf("argc: %d\n",gargc);
   83     for(int i=0; gargv[i]; i++)
   84     {
   85         printf("argv[%d]: %s\n", i, gargv[i]);
   86     }
   87 }
   88 
   89 int main()
   90 {
   91     char command_buffer[basesize];
   92     while(true)
   93     {
   94          PrintCommandLine();  // 1. 命令行提示符
   95          
   96          if(!GetCommandLine(command_buffer, basesize))    // 2. 获取用户命令
   97          {
   98             continue;
   99          }
  100          printf("%s\n",command_buffer);
  101 
  102          ParseCommandLine(command_buffer, strlen(command_buffer));  // 3. 分析命令
  103          debug();
  104 
  105          //ExecuteCommand();    // 4. 执行命令
  106     }
  107     return 0;
  108 }

四、执行命令

到底是谁在执行命令?是shell自己执行吗?

shell不能自己执行命令,如果shell自己可以自行命令,当命令错误的时候,整个程序就会全挂掉了。shell是一个单进程。shell 创建子进程来执行命令。

在命令行中输入命令,是如何解析的?谁解析的?如何传递给目标子进程的?

shell来帮助我们做解析,形成一张表,然后在进行程序替换的时候,直接调用系统的execvp()函数来执行我的程序,并把这张表传递给我自己对应的进程。

cpp 复制代码
   89 bool ExecuteCommand()    // 4. 执行命令
   90 {
   91     pid_t id = fork(); // 创建子进程
   92     if(id < 0) return false;
   93     if(id == 0)
   94     {
   95         // child
   96         // 1.执行命令 调用系统函数
   97         execvp(gargv[0], gargv);
   98         // 2.退出
   99         exit(1);
  100     }
  101     // father
  102     int status = 0;
  103     pid_t rid = waitpid(id, &status, 0); 
  104     if(rid > 0)
  105     {
  106         return true;
  107     }
  108     return false;
  109 }

五、内建命令

每个进程都会有一个当前路径的概念,改变当前路径【chdir】,[cd ..] 就会改变当前工作路径【改变子进程的工作路径】。

当我们在自己写的shell中,我们 [cd ..] 应该改变的是子进程的路径还是自己写的shell进程的路径?

改变shell的工作路径【shell本身就是父shell的一个子进程】,因为子进程执行完命令就会退出,shell当前的工作路径根本就没有改变;改变shell的工作路径,之后执行的命令都会使用shell当前的路径。即:在shell中,有些命令必须由子进程来执行,有些命令不能由子进程执行,要由shell自己执行 --- 内建命令。【shell自己内部调用自己的函数,并没有创建子进程】

我们自己所在的cwd是在进程的PCB当中的,pwd查找的时候也是在PCB里面查找的,PWD是一个环境变量,所以当路径发生变化的时候,要更新一下环境变量。

cpp 复制代码
  114 void AddEnv(const char *item)
  115 {
  116     int index = 0;
  117     while(genv[index])
  118     {
  119         index++;
  120     }
  121     // 指向为空的下标
  122     genv[index] = (char*)malloc(strlen(item)+1);// 不能使用局部变量,要重新申请          
  123     strncpy(genv[index], item, strlen(item)+1);                                  
  124                                                                                  
  125     genv[++index] = nullptr;                                                     
  126 }                                                                                   
  127                                                                                     
  128 bool CheckAndExecBuiltCommand()                                                     
  129 {                                                                                   
  130     if(strcmp(gargv[0], "cd") == 0)                                                 
  131     {                                                                               
  132         if(gargc == 2)                                                              
  133         {                                                                           
  134             chdir(gargv[1]);                                                        
  135         }                                                                           
  136         return true;                                                                
  137     }                                                                               
  138     else if(strcmp(gargv[0], "export") == 0)// 导入环境变量                  
  139     {                                                                        
  140         if(gargc == 2)                                                       
  141         {                                                                    
  142             AddEnv(gargv[1]);                                                
  143         }                                                                    
  144         return true;                                                         
  145     }                                                                        
  146     else if(strcmp(gargv[0], "env") == 0)                                    
  147     {                                                                        
  148         for(int i=0; genv[i]; i++)                                           
  149         {                                                                    
  150             printf("%s\n",genv[i]);
  151         }          
  152         return true;
  153     }              
  154     return false;
  155 }

我们虽然改变了工作路径,但是命令行提示符并没有发生改变,这是为什么呢?

🌠 小贴士:

• 环境变量是由shell自己维护的,即当路径发生变化时,环境变量表需要由shell自己取更新。

• 我们可以更改环境变量的原因:shell支持用户自己去更改,shell本来就是为用户服务的。

shell不是从0开始读配置文件的,它是从系统额shell直接启动的,所以我们写的shell启动的时候,它默认继承的是系统所对应的环境变量。

• ./myshell 它的环境变量,根本就没有维护环境变量表,它的环境变量其实是从(父进程)系统的shell直接继承的。

• 环境变量表的指针数组,默认是在系统的shell的全局数组给我们维护好的,它也是一张全局的表。

• putenv() 实则就是在父进程所对应的全局指针数组里面,找到一个没有使用的位置,把这个环境变量加进来。

• 导入到系统的shell里面,并不是子进程修改父进程的表,因为当发生修改时,会发生写时拷贝,当子进程不修改,父子进程就是共享的,一旦子进程进行修改,操作系统就会对父shell的指针数组进行写诗拷贝【把整张表全部拷贝一份给子进程】,此时父子进程的环境变量表就是分开的。

• getcwd(字符串,大小);获取当前工作路径【系统级接口,直接从进程的PCB里面拿】。

• 不能用子进程来导入环境变量,子进程不能影响当前的shell。

• 内建命令无法被子进程继承,环境变量可以,所以环境变量具有全局性。

cpp 复制代码
   36 string GetPWD()
   37 {         
   38     //string pwd = getenv("PWD");
   39     //return pwd.empty() ? "None" : pwd;
   40           
   41     // 系统调用
   42     // 1. 获取当前工作路径
   43     if(getcwd(pwd, sizeof(pwd)) == nullptr) return "None";
   44           
   45     // 2. 更新环境变量
   46     snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);
   47     putenv(pwdenv);// PWD=XXXX
   48     return pwd;                                                                          
   49 }

六、环境变量

我们在自己写的shell里面创建子进程,这个子进程继承的环境变量表是继承谁的?我自己写的shell的环境变量表?

自己写的shell 和 自己写的shell创建的子进程 的环境变量表,都是继承父shell的

那我们如何让自己写的shell 创建的子进程继承的环境变量表是自己写的shell的环境变量表呢?

父shell的环境变量表是从系统的配置文件里面来的【shell脚本】,我们从系统里把系统的环境变量拷一份到我们自己的shell里面,模拟一下从配置文件里面去读。

cpp 复制代码
  const int envnum = 64; 
// 自己维护的环境变量表
  char *genv[envnum];

  void InitEnv()
  {
      // 从父shell获取环境变量
       extern char **environ;
       int index = 0;
       while(environ[index])
       {
           // 导入环境变量,实则就是向shell自己的环境变量表当中,进行插入一个新的环境变量
           // 即 再malloc一段空间,让指针数组指向它对应的那个位置
           genv[index] = (char*)malloc(strlen(environ[index]+1));
           strncpy(genv[index], environ[index], strlen(environ[index])+1);
           index++;
       }
       genv[index] = nullptr;
  }

所以当我们执行命令的时候,我们要把自己的环境变量参数传给系统去执行,因此 在执行一个新的程序时,把命令行参数表、环境变量表都传给execvpe() 系统调用,程序执行时,就能获得这两个参数。

cpp 复制代码
  101 bool ExecuteCommand()    // 4. 执行命令
  102 {
  103     pid_t id = fork(); // 创建子进程
  104     if(id < 0) return false;
  105     if(id == 0)
  106     {
  107         // child
  108         // 1.执行命令 调用系统函数
  109         //execvp(gargv[0], gargv);
  110         execvpe(gargv[0], gargv, genv);                                                  
  111         // 2.退出               
  112         exit(1);                
  113     }                           
  114     // father                   
  115     int status = 0;             
  116     pid_t rid = waitpid(id, &status, 0);
  117     if(rid > 0)                 
  118     {                           
  119         return true;            
  120     }                           
  121     return false;               
  122 }                               

🌠小贴士:

• 环境变量:本质是shell自己维护的一张全局的表,最后可以直接通过地址空间,或者通过execvpe(),把自己的环境变量信息传递给目标被替换进程。

• 本地变量:本地变量当字符串。不会通过execvp() 传递给子进程。

七、总结

1、在命令行中,一个命令是如何执行的?对于普通命令来讲,就是解析命令行,然后通过exec*系列的函数接口,进行fork()程序替换。

2、命令行参数是从命令行依次获取的,被shell自己解析自己维护。

3、环境变量表是从系统的配置文件读取出来的,由shell自己维护,维护好就是一个全局的指针数组,通过 execvpe() 接口函数来调用,这个系统的调用接口把环境变量传递给所有的子进程。

4、echo命令,是内建命令。系统除了维护环境变量表、命令行参数表,还维护了一张本地变量表,这张本地变量表无法通过 exec* 这样的接口传递给子进程,所以子进程看不到。在本地变量的这张表,让shell自己去维护,在echo的时候,就去打印。本地变量表属于在shell自己内部维护的全局数据当中的一个字符串。

八、完整代码

cpp 复制代码
  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <string>
  6 #include <unistd.h>
  7 #include <sys/types.h>
  8 #include <wait.h>
  9 
 10 using namespace std;
 11 
 12 const int basesize = 1024;
 13 const int argvnum = 64;
 14 const int envnum = 64;
 15 
 16 // 全局的命令行参数表
 17 char *gargv[argvnum];// 全局的环境变量表
 18 int gargc = 0; // 记录环境变量的个数
 19 
 20 // 自己维护环境变量表
 21 char *genv[envnum];
 22 
 23 // 系统的环境变量默认是从配置文件来的【shell脚本】,
 24 // 从系统里把系统环境变量拷一份到我的环境变量里面,模拟一下从配置文件里面去读
 25 
 26 // 全局的当前shell工作路径 
 27 char pwd[basesize];
 28 char pwdenv[basesize];
 29 
 30 string GetUserName()
 31 {
 32     string user = getenv("USER");
 33     return user.empty() ? "None" : user;
 34 }
 35 
 36 string GetHostName()
 37 {
 38     string hostname = getenv("HOSTNAME");
 39     return hostname.empty() ? "None" : hostname;
 40 }                                                                                          
 41 
 42 string GetPwd()
 43 { 
 44     // 获取当前的工作路径不能从环境变量里面获取,因为环境变量要更新
 45     // string pwd = getenv("PWD"); 
 46     // return pwd.empty() ? "None" : pwd;
 47  
 48     // 系统调用
 49 
 50     // 直接从系统里面获取环境变量
 51     // 获取完之后,把环境变量更新                                                          
 52     // getcwd(字符串,大小) 获取当前工作路径【系统级接口,直接从进程的PCB里面拿】被封装的接    口gunc封装的接口
 53     // 失败返回值为NULL,成功就把当前工作路径写到buff里面
 54     
 55     // 1、获取当前工作路径
 56     if(nullptr == getcwd(pwd, sizeof(pwd))) return "None"; // 获得当前工作路径
 57     
 58     // 2、更新环境变量
 59     snprintf(pwdenv, sizeof(pwdenv), "PWD=%s", pwd);
 60     putenv(pwdenv); // PWD=XXXX
 61     return pwd;
 62 
 63 // 环境变量是由shell自己维护的,即当路径发生变化时,环境变量表需要由shell自己去更新
 64 // 我们可以更改环境变量的原因:shell支持用户自己去更改,shell本来就是为用户服务的
 65 // shell不是从0开始读配置文件的,它时从系统的shell直接启动的,
 66 // 所以我们写的shell启动的时候,它默认继承的是系统所对应的环境变量
 67 // ./myshell它自己的环境变量,根本就没有维护环境变量表,
 68 // 它的环境变量其实是从(父进程)系统的shell直接继承
 69 //
 70 // getenv 
 71 //
 72 // 环境变量表的指针数组,默认是在系统的shell的全局数组给我们维护好的,它也是一张全局的表
 73 // putenv()实则就是在父进程所对应的全局指针数组里面,
 74 // 找一个没有使用的位置,然后把这个环境变量加进来
 75 //
 76 // 导入到系统额shell里面,并不是子进程修改父进程的表,
 77 // 因为发生修改时,会发生写时拷贝机制,
 78 // 当子进程不修改,父子进程时共享的,一旦子进程进行修改,
 79 // 操作系统就会对父shell的指针数组进行写时拷贝。【把整张表全部拷贝一份给子进程】
 80 // 此时父子进程的环境变量表就是分开的
 81 
 82 }
 83 
 84 string MakeCommandLine()
 85 {
 86     char command_line[basesize];// 输入的命令行长度
 87     // snprintf(写入到指定的缓冲区里,指定长度,按照指定的格式)
 88     snprintf(command_line, basesize, "[%s@%s %s]# ",\
 89             GetUserName().c_str(), GetHostName().c_str(), GetPwd().c_str());// c_str() 
    为C风格的字符串
 90         return command_line;
 91 }
 92 
 93 void PrintCommandLine()    // 1.命令行提示符
 94 {
 95     printf("%s",MakeCommandLine().c_str());
 96     fflush(stdout);                                                                        
 97 }
 98          
 99 bool GetCommandLine(char command_buffer[], int size)      // 2.获取用户命令
100 {
101     // 从键盘当中获取出来的字符串,放到command_buffer缓冲区里面
102     // 将用户输入的命令行,当作完整的字符串
103     // "ls -a -l -n" 包括空格
104     // fgets(获取字符串, 指定大小, 从特定的文件流当中获取) 获取失败返回null,获取成功返回获    取成功的字符串的起始地址
105     char *result = fgets(command_buffer, size, stdin);
106     if(!result)
107     {
108         return false;
109     }
110     command_buffer[strlen(command_buffer)-1] = 0;// 最后输入的回车,把回车的字符改为0,字符    串为空,即输入就是纯净版的没有换行
111     if(strlen(command_buffer) == 0) return false; // 若输入为空,就终止
112     return true;
113 }
114 
115 void ParseCommandLine(char command_buffer[], int len)    // 3.分析命令
116 {
117     (void)len;
118     // 解析出来的命令,放到全局的环境变量表当中
119     // strtok(一个字符串分多个,分隔符) 返回值:按照该分隔符从源字符串里切出来的第一个区域
120     // strtok(str, " ");      如果切割有效的字符串,返回具体的字符串的第一个地址,
121     // strtok(nullptr, " ");    否则,返回nullptr,即切到最后没有了,自动返回nullptr
122     // "ls -a -l -n"
123     memset(gargv, 0 , sizeof(gargv)); // 初始化/清空环境变量表里面的内容
124     gargc = 0;
125 
126     const char *sep = " ";
127     // 把首个子字符串拿出来
128     gargv[gargc++] = strtok(command_buffer, sep);// 放到全局的环境列表里面
129     
130     // =是刻意写的  循环放入环境变量表中
131     while((bool)(gargv[gargc++] = strtok(nullptr, sep)));
132     gargc--;
133 }
134 
135 void debug()                                                                               
136 {
137     printf("argc: %d\n",gargc);
138     for(int i=0; gargv[i]; i++)
139     {
140         printf("argv[%d]: %s\n", i, gargv[i]);
141     }
142 }
143 
144 bool ExecuteCommand()      // 4.执行命令
145 {
146     // shell 不能自己执行这个命令,如果shell可以自己执行命令,当命令错误的时候就全挂掉了,
147     // shell是一个单进程的。
148     // 让子进程执行命令
149     pid_t id = fork();// 创建子进程
150     if(id < 0) return false;
151     if(id == 0)
152     {
153         // child
154         // 1. 执行命令
155         // exec*
156         // execvp(gargv[0], gargv);
157         // 在命令行当中输入的命令,是如何解析的?谁解析的?如何传递给目标子进程的?
158         // shell来帮我们做解析,形成一张表,然后在进行程序替换的时候,
159         // 直接以execvp的形式执行起来我的程序,并把这张表传递给对应的自己的进程
160         
161         // 如果我们在自己写的shell里面创建子进程,
162         // 那这个子进程继承的环境变量表是父shell的,自己写的shell也是继承父shell的环境表
163         // 那我们如何让我们自己写的shell,创建的子进程继承的环境变量表是我自己写的shell的环    境变量表呢?
164         execvpe(gargv[0], gargv, genv);// 把自己写的环境变量参数传给它
165         // 在执行一个新的程序时,把命令行参数表、环境变量表都传给execvpe系统,
166         // 所以自己的程序是父进程通过系统调用,把这两个参数交给了你的程序,
167         // 程序执行时,就能获得这两个参数
168         
169 
170 
171         // 2. 退出
172         exit(1);
173     }
174     
175     // father
176     int status = 0;
177     pid_t rid = waitpid(id, &status, 0);
178     if(rid > 0)
179     {
180         // Do Nothing                                                                      
181         return true;
182     }
183     return false;
184 }
185 
186 void AddEnv(const char *item)
187 {
188     int index = 0;
189     while(genv[index])
190     {
191         index++;
192     }
193     // 指向为空的下标
194     genv[index] = (char*)malloc(strlen(item)+1);// 不能使用局部的变量,要重新申请
195     strncpy(genv[index], item, strlen(item)+1);
196 
197     genv[++index] = nullptr;
198 }
199 
200 // shell自己执行命令,本质是shell调用自己的函数
201 bool CheckAndExexcBuiltCommand() // shell调用自己的内部函数,把自己的路径发生变化
202 {
203     if(strcmp(gargv[0], "cd") == 0)
204     {
205         // 内建命令
206         if(gargc == 2) // 
207         {
208             // 系统接口
209             chdir(gargv[1]); // 切换路径
210         }
211         return true;
212     } 
213     else if(strcmp(gargv[0], "export") == 0)
214     {
215         // export也是内建命令
216         if(gargc == 2)
217         {
218             AddEnv(gargv[1]);
219         }
220         return true;
221     }
222     else if(strcmp(gargv[0], "env") == 0)                                                  
223     {
224         for(int i=0; genv[i]; i++)
225         {
226             printf("%s\n",genv[i]);
227         }
228         return true;
229     }
230     return false; 
231 }
232 
233 // 从系统里把系统环境变量拷一份到我的环境变量里面,模拟一下从配置文件里面去读
234 // 作为一个shell,获取环境变量应该从系统的配置文件来获取
235 // 我们现在是直接从父shell中获取环境变量
236 void InitEnv()
237 {
238     // 从父进程获取环境变量
239     extern char **environ;
240     int index = 0;
241     while(environ[index])
242     {
243         // 导入环境变量,实则就是向shell自己的环境变量表当中,进行插入一个新的环境变量
244         // 即 再malloc 一段空间,让指针数组指向它对应的那个位置
245         genv[index] = (char*)malloc(strlen(environ[index]+1));
246         strncpy(genv[index], environ[index], strlen(environ[index])+1);
247         index++;        
248     }
249     genv[index] = nullptr;
250 }
251 
252 int main()
253 {
254     InitEnv();
255     char command_buffer[basesize];// 命令行缓冲区大小
256     while(true)
257     {
258          PrintCommandLine();    // 1.命令行提示符
259          
260          // command_buffer --> 输出型参数
261          if(!GetCommandLine(command_buffer, basesize))      // 2.获取用户命令
262          {  // 依次获取每个子字符串
263              continue;// 获取到空格继续获取
264          }
265          //printf("%s\n",command_buffer);
266 
267          // "ls -a -l -n" --> "ls" "-a" "-l" "-n" 拆成一个一个的子字符串                   
268          ParseCommandLine(command_buffer, strlen(command_buffer));    // 3.分析命令
269          //debug();
270 
271          // 先判断该命令是否是内建命令
272          if(CheckAndExexcBuiltCommand())
273          {
274              continue;
275          }
276          
277          ExecuteCommand();      // 4.执行命令   
278     
279     }
280 
281     return 0;
282 }
相关推荐
观测云2 分钟前
Docker Container 可观测性最佳实践
运维·docker·容器
心灵彼岸-诗和远方13 分钟前
DevOps工程技术价值流:Ansible自动化与Semaphore集成
linux·运维·网络·软件工程·devops
思码逸研发效能19 分钟前
在 DevOps 中,如何应对技术债务和系统复杂性,以确保可持续的研发效能和创新?
运维·算法·研发效能·devops·研发效能度量·效能度量
m0_7482515220 分钟前
Linux(CentOS)安装 MySQL
linux·mysql·centos
爱写代码的小白.23 分钟前
RustDesk内置ID服务器,Key教程
linux·运维·服务器
wq54wq36 分钟前
智能流程管理:CRM系统助力订单与回款自动化
运维·自动化
朝九晚五ฺ1 小时前
【Linux探索学习】第二十四弹——软硬链接:Linux 中的软链接与硬链接详解
linux·运维·chrome·学习
Hacker_Nightrain2 小时前
linux 网络安全不完全笔记
linux·笔记·web安全
一入程序无退路2 小时前
c语言传参数路径太长,导致无法获取参数
linux·c语言·数据库
上海运维Q先生2 小时前
面试题整理19----Metric的几种类型?分别是什么?
运维·服务器·面试