ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【Linux】自定义shell(讲解原理) [打印本页]

作者: 不到断气不罢休    时间: 2024-12-13 02:30
标题: 【Linux】自定义shell(讲解原理)


  
一、打印命令提示符和获取命令行命令字符串

1.1 设计

我们首先使用操作体系的bash看到了命令行提示符的组成为[用户名@主机名 当前工作目次]$,获取用户名、主机名和当前工作目次的函数在体系调用中都有,这里我们自己设计一个,这三个数据都是环境变量,我们可以通过getenv来获取到他们,获取后将他们按照操作体系bash的格式输出出来即可,通过下图我们可以发现,我们的当前工作目次与操作体系的有所区别,我们的当前工作目次是一条路径,可以通过裁剪得到与操作体系一样的效果,我这里为了区分与操作体系的区别,这里就不做裁剪了。

输出完命令行提示符后,就必要向bash中输入命令了,这里我们就必要一个输入函数来读取命令字符串,必要注意的是这里不能使用scanf函数,因为scanf函数不能读取空格之后的内容,可以选择gets/fgets函数来读取,当我们输入完命令字符串后必要按回车,那么获取到的字符串中也会获取到这个’\n’,所以我们还必要将这个’\n’处理掉。
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #define NUM 1024
  5. const char* getUsername()
  6. {
  7.     char* username = getenv("USER");
  8.     if(username)
  9.         return username;
  10.     else
  11.         return "none";
  12. }
  13. const char* getHostname()
  14. {
  15.     char* hostname = getenv("HOSTNAME");
  16.     if(hostname)
  17.         return hostname;
  18.     else
  19.         return "none";
  20. }
  21. const char* getCwd()
  22. {
  23.     char* cwd = getenv("PWD");                                                                                                               
  24.     if(cwd)                                                                                                                                 
  25.         return cwd;                                                                                                                                      
  26.     else                                                                                                                                    
  27.         return "none";     
  28. }
  29. int main()  
  30. {
  31.     char usercommand[NUM];
  32.     printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());
  33.    
  34.     // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf
  35.     fgets(usercommand,sizeof(usercommand),stdin);
  36.     // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉
  37.     usercommand[strlen(usercommand)-1] = '\0';
  38.         // 用于测试输入的字符串是否符合我们的预期
  39.     printf("%s",usercommand);
  40.     return 0;
  41. }
复制代码

1.2 封装

这里将打印命令行提示符与获取命令行字符串的工作统一封装到getUserCommand这个函数中。
  1. int getUserCommand(char* command , int num)   
  2. {   
  3.     printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());   
  4.         
  5.     // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf   
  6.     char* r = fgets(command,num,stdin);   
  7.     if(r == NULL) return -1;  // 读取失败返回-1   
  8.    
  9.     // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉   
  10.     command[strlen(command)-1] = '\0';   
  11.     // 由于读入字符串时,必定会有一个\n所以这么不可能会越界                                                                                            
  12.                               
  13.     // 用于测试输入的字符串是否符合我们的预期
  14.     printf("%s",command);
  15.    
  16.     return 1;                 
  17. }
  18. int main()
  19. {
  20.     char usercommand[NUM];
  21.    
  22.     getUserCommand(usercommand,NUM);
  23.     return 0;
  24. }
复制代码

二、分割字符串

2.1 设计

当我们获取到了命令字符串后,必要将字符串以空格为分隔符将字符串分割为子字符串,并将每一个子字符串的地址存入到一个指针数组中,这里给出一个字符串被分割的例子:"ls -l -a" -> "ls" "-l" "-a"。我们可以使用strtok函数来将字符串分割,使用strtok函数处理同一个字符串时,第一次必要传入字符串的地址,后面再次调用则只必要传入NULL即可。
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #define NUM 1024                    
  5. #define SIZE 64     
  6. #define SEP " "
  7. const char* getUsername()
  8. {
  9.     char* username = getenv("USER");
  10.     if(username)
  11.         return username;
  12.     else
  13.         return "none";
  14. }
  15. const char* getHostname()
  16. {
  17.     char* hostname = getenv("HOSTNAME");
  18.     if(hostname)
  19.         return hostname;
  20.     else
  21.         return "none";
  22. }
  23. const char* getCwd()
  24. {
  25.     char* cwd = getenv("PWD");                                                                                                               
  26.     if(cwd)                                                                                                                                 
  27.         return cwd;                                                                                                                                      
  28.     else                                                                                                                                    
  29.         return "none";     
  30. }
  31. int main()  
  32. {                                                                                                                                                        
  33.     while(1)  
  34.     {         
  35.         char usercommand[NUM];  
  36.         char* argv[SIZE];      
  37.                                 
  38.         int x = getUserCommand(usercommand,NUM);  
  39.                                                   
  40.         if(x == -1) continue;                                
  41.                                     
  42.         int argc = 0;               
  43.         argv[argc++] = strtok(usercommand,SEP);  
  44.         while(argv[argc++] = strtok(NULL,SEP));         
  45.         // strtok函数分割失败时会返回NULL,正好是我们字符串数组结尾所需要的   
  46.             // 所以分割的方式可以使用赋值作为循环判断条件进行循序   
  47.             // 若是大家不习惯可以使用下面这一种分割方式   
  48.            
  49.         // while(argv[argc++] = strtok(NULL,SEP));  
  50.         // while(1)                                    
  51.         // {                                          
  52.         //     argv[argc] = strtok(NULL,SEP);         
  53.         //     if(argv[argc] == NULL)                  
  54.         //         break;                     
  55.         //     argc++;                        
  56.         // }                                   
  57.                                     
  58.         for(int i = 0 ; argv[i] ; i++)  
  59.         {                              
  60.             printf("%d : %s \n",i,argv[i]);  
  61.         }                                    
  62.     }
  63.     return 0;
  64. }
复制代码

2.2 封装

  1. void SplitCommand(char* in , char* out[])      
  2. {      
  3.     int argc = 0;      
  4.    
  5.     out[argc++] = strtok(in,SEP);      
  6.    
  7.     while(out[argc++] = strtok(NULL,SEP));        
  8.     // strtok函数分割失败时会返回NULL,正好是我们字符串数组结尾所需要的   
  9.     // 所以分割的方式可以使用赋值作为循环判断条件进行循序   
  10.     // 若是大家不习惯可以使用下面这一种分割方式   
  11.                                                                                                                                                       
  12.     // while(1)   
  13.     // {   
  14.     //     out[argc] = strtok(NULL,SEP);   
  15.     //     if(out[argc] == NULL)                                                                                                              
  16.     //         break;   
  17.     //     argc++;                                                                                                                           
  18.     }                                                                                                                                                  
  19.                                                                                                                                            
  20. // 用于测试字符串是否被分割                                                                                                                                             
  21. #ifdef debug
  22.      for(int i = 0 ; out[i] ; i++)                                                                                                         
  23.      {                                                                                                                                      
  24.          printf("%d : %s \n",i,out[i]);                                                                                                     
  25.      }        
  26. #endif                                                                                                                                    
  27. }                                                                                                                                          
  28.                                                                                                                                           
  29. int main()                                                                                                                                 
  30. {                                                                                                                                          
  31.     while(1)                                                                                                                              
  32.     {                                                                                                                                      
  33.         char usercommand[NUM];                                                                                                            
  34.         char* argv[SIZE];   
  35.             
  36.         int x = getUserCommand(usercommand,NUM);   
  37.    
  38.         SplitCommand(usercommand,argv);
  39.     }
  40.     return 0;
  41. }
复制代码

三、执行指令

3.1 设计

将命令字符串分割后,就必要执行命令了,我们知道bash必要一直运行,这里添加一个循环让他一直运行,我们可以使用前面学习过的进程替换来执行命令,但是不能使用当前进程来进程替换,当前进程还必要继承运行,所以我们可以创建一个子进程来执行命令,由于我们将字符串分割为子字符串存储在了指针数组中,这里可以使用execvp函数来进行进场替换。
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <sys/types.h>
  6. #include <sys/wait.h>
  7. #define NUM 1024
  8. #define SIZE 64
  9. #define SEP " "
  10. #define debug 1
  11. const char* getUsername()
  12. {
  13.     char* username = getenv("USER");
  14.     if(username)
  15.         return username;
  16.     else
  17.         return "none";
  18. }
  19. const char* getHostname()
  20. {
  21.     char* hostname = getenv("HOSTNAME");
  22.     if(hostname)
  23.         return hostname;
  24.     else
  25.         return "none";
  26. }
  27. const char* getCwd()
  28. {
  29.     char* cwd = getenv("PWD");                                                                                                               
  30.     if(cwd)                                                                                                                                 
  31.         return cwd;                                                                                                                                      
  32.     else                                                                                                                                    
  33.         return "none";     
  34. }
  35. int getUserCommand(char* command , int num)   
  36. {   
  37.     printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());   
  38.         
  39.     // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf   
  40.     char* r = fgets(command,num,stdin);   
  41.     if(r == NULL) return -1;  // 读取失败返回-1   
  42.    
  43.     // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉   
  44.     command[strlen(command)-1] = '\0';   
  45.     // 由于读入字符串时,必定会有一个\n所以这么不可能会越界   
  46.         // 用于测试输入的字符串是否符合我们的预期
  47.     // printf("%s",command);                                                                                       
  48.                               
  49.     return 1;                 
  50. }
  51. void SplitCommand(char* in , char* out[])      
  52. {      
  53.     int argc = 0;      
  54.    
  55.     out[argc++] = strtok(in,SEP);      
  56.    
  57.     while(out[argc++] = strtok(NULL,SEP));        
  58.     // strtok函数分割失败时会返回NULL,正好是我们字符串数组结尾所需要的   
  59.     // 所以分割的方式可以使用赋值作为循环判断条件进行循序   
  60.     // 若是大家不习惯可以使用下面这一种分割方式   
  61.                                                                                                                                                       
  62.     // while(1)   
  63.     // {   
  64.     //     out[argc] = strtok(NULL,SEP);   
  65.     //     if(out[argc] == NULL)                                                                                                              
  66.     //         break;   
  67.     //     argc++;                                                                                                                           
  68.     }                                                                                                                                                  
  69.                                                                                                                                            
  70.                                                                                                                                              
  71. #ifdef debug
  72.      for(int i = 0 ; out[i] ; i++)                                                                                                         
  73.      {                                                                                                                                      
  74.          printf("%d : %s \n",i,out[i]);                                                                                                     
  75.      }        
  76. #endif                                                                                                                                    
  77. }  
  78. int main()                                                                                                                                 
  79. {                                                                                                                                          
  80.     while(1)                                                                                                                              
  81.     {                                                                                                                                                  
  82.         char usercommand[NUM];                                                                                                            
  83.         char* argv[SIZE];   
  84.         // 打印命令提示符和获取命令行命令字符串   
  85.         int x = getUserCommand(usercommand,NUM);   
  86.    
  87.         if(x <= 0) continue;   
  88.             // 分割命令字符串
  89.         SplitCommand(usercommand,argv);
  90.         pid_t id = fork();
  91.         if(id < 0) return -1;
  92.         else if(id == 0)
  93.         {
  94.             execvp(argv[0],argv);
  95.             // 若替换失败则子进程退出
  96.             exit(-1);
  97.         }
  98.         else
  99.         {
  100.             pid_t rid = wait(NULL);
  101.             if(rid>0){};      
  102.         }
  103.     }
  104.     return 0;
  105. }
复制代码

3.2 封装

  1. int execute(char* argv[])
  2. {
  3.     pid_t id = fork();
  4.     if(id < 0) return -1;
  5.     else if(id == 0)
  6.     {   
  7.         execvp(argv[0],argv);   
  8.         // 若替换失败则子进程退出   
  9.         exit(-1);   
  10.     }   
  11.     else   
  12.     {   
  13.         pid_t rid = wait(NULL);   
  14.         if(rid>0){};     
  15.     }   
  16.     return 0;   
  17. }   
  18. int main()   
  19. {   
  20.     while(1)   
  21.     {   
  22.         char usercommand[NUM];   
  23.         char* argv[SIZE];   
  24.         // 打印命令提示符和获取命令行命令字符串        
  25.         int x = getUserCommand(usercommand,NUM);   
  26.                                                                                                                                                       
  27.         if(x <= 0) continue;   
  28.             // 分割命令字符串
  29.         SplitCommand(usercommand,argv);
  30.             // 执行命令
  31.         execute(argv);
  32.     }
  33.     return 0;
  34. }
复制代码

四、处理內键命令的执行

当我们使用上面的代码执行命令时,发现大部分命令都可以被执行,但是比方cd、export、echo这样的内建命令却不能被执行,原因是内建命令是作用与bash的也就是这里的父进程,并且内建命令是bash的一部分,与常见命令不同,执行内建命令时不必要创建新的子进程,所以这些内建命令必要被特殊处理一下。将命令字符串分割后就判断当前命令是否为内建命令,是则直接执行内建命令,否则认定为常见命令向下继承执行。内建命令如那里理,这里就不多讲解,具体处理方法在下面的代码中有具体的注释,有爱好的可以看一下。
  1. #include <stdio.h>   
  2. #include <string.h>   
  3. #include <stdlib.h>   
  4. #include <unistd.h>   
  5. #include <sys/types.h>   
  6. #include <sys/wait.h>   
  7.    
  8. #define NUM 1024   
  9. #define SIZE 64     
  10. #define SEP " "     
  11. // #define debug 1   
  12.    
  13. // 由于环境变量PWD需要一直存在,这里定义一个全局变量来存储
  14. char cwd[1024];   
  15. // 定义一个全局二维数组中,用于存储添加的环境变量
  16. char myenv[128][1024];   
  17. // 记录二维数组中有多少个环境变量
  18. int cnt = 0;   
  19. int lastcode = 0; // 记录退出码   
  20.                                                                                                                                                       
  21. char* getHomename()   
  22. {                                       
  23.     char* homename = getenv("HOME");   
  24.     if(homename)            
  25.         return homename;   
  26.     else                          
  27.         return (char*)"none";   
  28. }                                
  29. const char* getUsername()   
  30. {                                       
  31.     char* username = getenv("USER");
  32.     if(username)            
  33.         return username;   
  34.     else                                                                                                                                               
  35.         return "none";   
  36. }   
  37. const char* getHostname()
  38. {
  39.     char* hostname = getenv("HOSTNAME");
  40.     if(hostname)
  41.         return hostname;
  42.     else
  43.         return "none";
  44. }
  45. const char* getCwd()
  46. {
  47.     char* cwd = getenv("PWD");
  48.     if(cwd)
  49.         return cwd;
  50.     else
  51.         return "none";
  52. }
  53. int getUserCommand(char* command , int num)
  54. {
  55.     printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());
  56.    
  57.     // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf                                                                        
  58.     char* r = fgets(command,num,stdin);
  59.     if(r == NULL) return -1;  // 读取失败返回-1
  60.     // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉
  61.     command[strlen(command)-1] = '\0';
  62.     // 由于读入字符串时,必定会有一个\n所以这么不可能会越界
  63.     // 用于测试输入的字符串是否符合我们的预期   
  64.     // printf("%s\n",command);   
  65.     // 当命令行只输入回车时,则没有必要创建子进程来执行任务
  66.     if(strlen(command) == 0)
  67.         return 0;
  68.     return 1;
  69. }
  70. void SplitCommand(char* in , char* out[])   
  71. {   
  72.     int argc = 0;   
  73.     out[argc++] = strtok(in,SEP);   
  74.           while(out[argc++] = strtok(NULL,SEP));                                                                                                            
  75.     // strtok函数分割失败时会返回NULL,正好是我们字符串数组结尾所需要的
  76.     // 所以分割的方式可以使用赋值作为循环判断条件进行循序
  77.     // 若是大家不习惯可以使用下面这一种分割方式
  78.    
  79.     //while(1)   
  80.     //{   
  81.     //    out[argc] = strtok(NULL,SEP);   
  82.     //    if(out[argc] == NULL)                                                                                                              
  83.     //        break;   
  84.     //    argc++;                                                                                                                           
  85.     //}                                                                                                                                      
  86. #ifdef debug
  87.     for(int i = 0 ; out[i] ; i++)                                                                                                         
  88.     {                                                                                                                                      
  89.         printf("%d : %s \n",i,out[i]);                                                                                                     
  90.     }        
  91. #endif
  92. }
  93. int execute(char* argv[])
  94. {
  95.     pid_t id = fork();
  96.                                                                                                                                                       
  97.     if(id < 0) return -1;
  98.     else if(id == 0)
  99.     {
  100.         execvp(argv[0],argv);
  101.         // 若替换失败则子进程退出
  102.         exit(-1);
  103.     }
  104.     else
  105.     {
  106.         // sleep(1);
  107.         int status = 0;
  108.         pid_t rid = wait(&status);
  109.         if(rid>0)
  110.         {
  111.             lastcode = WEXITSTATUS(status);
  112.         }
  113.         
  114.     }
  115.     return 0;
  116. }
  117. void cd(char* path)
  118. {
  119.         // 将当前的工作目录改为path
  120.     chdir(path);
  121.     // tmp作为一个临时空间
  122.     char tmp[1024];
  123.     // 将当前工作目录写入到tmp中
  124.     getcwd(tmp,sizeof(tmp));
  125.     // 将PWD=与tmp进行组合,形成环境变量的格式存储到全局变量cwd                                                                                                                     
  126.     sprintf(cwd,"PWD=%s",tmp);
  127.     // 将cwd添加到环境变量中,覆盖掉原来的环境变量PWD
  128.     putenv(cwd);
  129. }
  130. // 内键命令并执行1,非内键命令0
  131. int dobuildin(char* argv[])
  132. {
  133.     if(strcmp(argv[0],"cd") == 0)
  134.     {
  135.         char* path = NULL;
  136.         // cd命令后面没有添加路径,默认更改当前工作目录为家目录
  137.         if(argv[1] == NULL)
  138.             path = getHomename();
  139.         else
  140.             path = argv[1];
  141.         cd(path);
  142.         return 1;
  143.     }
  144.     else if(strcmp(argv[0],"export") == 0)
  145.     {
  146.             // 如果export后面没有内容则不做任何处理
  147.         if(argv[1] == NULL)
  148.             return 1;
  149.         else
  150.         {
  151.                 // 将需要添加的环境变量保存到全局的二维数组中
  152.             strcpy(myenv[cnt],argv[1]);
  153.             // 将这个环境变量添加到进程的环境变量表中
  154.             putenv(myenv[cnt++]);
  155.             return 1;
  156.         }
  157.     }
  158.         else if(strcmp(argv[0],"echo") == 0)
  159.     {
  160.             // 当echo后面没有内容时,默认输出回车
  161.         if(argv[1] == NULL)
  162.         {
  163.             printf("\n");
  164.             return 1;
  165.         }
  166.         // 当echo后面的字符串的第一个字符为$时
  167.         // 就是查看进程的退出码或是环境变量的                                                                                                                                             
  168.         else if(*(argv[1]) == '$' && strlen(argv[1]) >= 2)
  169.         {
  170.             // 输出退出码
  171.             if(*(argv[1]+1) == '?')
  172.             {
  173.                 printf("%d\n",lastcode);            
  174.                 lastcode = 0;
  175.             }
  176.             else  // 输出环境变量  
  177.             {
  178.                 const char* enval = getenv(argv[1]+1);
  179.                 if(enval)
  180.                 {
  181.                     printf("%s",enval);
  182.                 }
  183.                 else
  184.                 {
  185.                     printf("\n");
  186.                 }
  187.             }
  188.         }
  189.         // 不符合上面情况,通常就是将echo后面的字符串直接输出
  190.         else
  191.         {
  192.             printf("%s",argv[1]);
  193.         }
  194.         return 1;
  195.         }                                                                                                                                                  
  196.     else if(0){}
  197.     return 0;
  198. }
  199. int main()                                                                                                                                 
  200. {                                                                                                                                          
  201.     while(1)                                                                                                                              
  202.     {                                                                                                                                      
  203.         char usercommand[NUM];                                                                                                            
  204.         char* argv[SIZE];   
  205.         // 打印命令提示符和获取命令行命令字符串   
  206.         int x = getUserCommand(usercommand,NUM);   
  207.    
  208.         if(x <= 0) continue;   
  209.    
  210.         SplitCommand(usercommand,argv);
  211.         x = dobuildin(argv);
  212.         if(x == 1)
  213.             continue;
  214.         execute(argv);
  215.     }
  216.     return 0;
  217. }
复制代码

五、重定向(本文章所有代码)

写完前面的代码后,发现这个代码并不能解决重定向的问题,没了解重定向的最悦目一下后面一篇文章了解一下重定向是什么,在分割命令字符串之前,我们必要判断这个命令字符串是否必要进行重定向,必要重定向则必要对字符串进行处理,比方"ls -l -a > fortest.txt" -> "ls -l -a" 重定向范例 "fortest.txt",我们会得到三个部分,命令字符串、重定向范例、和文件名,在代码定义四种重定向范例,无重定向、输入重定向、输出重定向和追加重定向,默认情况下是无重定向,定义一个全局变量存储重定向范例,定义一个全局指针来指向文件名,然后我们针对不同的重定向范例使用dup2函数进行不同的处理,具体不同重定向的处理过程请查察代码中重定向的一部分。
  1. #include <stdio.h>                                                                                                                                    
  2. #include <string.h>                  
  3. #include <stdlib.h>   
  4. #include <unistd.h>   
  5. #include <sys/types.h>                        
  6. #include <sys/wait.h>   
  7. #include <sys/stat.h>   
  8. #include <fcntl.h>   
  9. #include <ctype.h>   
  10.      
  11. #define NUM 1024   
  12. #define SIZE 64        
  13. #define SEP " "     
  14. // #define debug 1   
  15.                                                             
  16. #define NoneRedir   0           
  17. #define InputRedir  1            
  18. #define OutputRedir 2   
  19. #define AppendRedir 3   
  20.    
  21. int redir = NoneRedir;            
  22. char* filename = NULL;         
  23.      
  24. // 由于环境变量PWD需要一直存在,这里定义一个全局变量来存储
  25. char cwd[1024];   
  26. // 定义一个全局二维数组中,用于存储添加的环境变量
  27. char myenv[128][1024];   
  28. // 记录二维数组中有多少个环境变量
  29. int cnt = 0;   
  30. int lastcode = 0; // 记录退出码   
  31. char* getHomename()
  32. {                              
  33.     char* homename = getenv("HOME");   
  34.     if(homename)
  35.         return homename;   
  36.     else   
  37.         return (char*)"none";                                                                                                                          
  38. }
  39. const char* getUsername()
  40. {
  41.     char* username = getenv("USER");
  42.     if(username)
  43.         return username;
  44.     else
  45.         return "none";
  46. }
  47. const char* getHostname()
  48. {
  49.     char* hostname = getenv("HOSTNAME");
  50.     if(hostname)
  51.         return hostname;
  52.     else
  53.         return "none";
  54. }
  55. const char* getCwd()
  56. {
  57.     char* cwd = getenv("PWD");                                                                                                                        
  58.     if(cwd)
  59.         return cwd;
  60.     else
  61.         return "none";
  62. }
  63. int getUserCommand(char* command , int num)
  64. {
  65.     printf("[%s@%s %s]# ",getUsername(),getHostname(),getCwd());
  66.    
  67.     // scanf("%s",usercommand); scanf读到空格就会自动停止,所以这里不使用scanf
  68.     char* r = fgets(command,num,stdin);
  69.     if(r == NULL) return -1;  // 读取失败返回-1
  70.     // 读取字符串时,会自动获取到'\n',我们需要将这个'\n'去掉
  71.     command[strlen(command)-1] = '\0';
  72.     // 由于读入字符串时,必定会有一个\n所以这么不可能会越界
  73.     // 用于测试输入的字符串是否符合我们的预期   
  74.     // printf("%s\n",command);   
  75.     // 当命令行只输入回车时,则没有必要创建子进程来执行任务
  76.     if(strlen(command) == 0)
  77.         return 0;
  78.     return 1;
  79. }
  80. void SplitCommand(char* in , char* out[])   
  81. {   
  82.     int argc = 0;   
  83.     out[argc++] = strtok(in,SEP);   
  84.         while(out[argc++] = strtok(NULL,SEP));   
  85.                                                                                                                                  
  86. #ifdef debug
  87.     for(int i = 0 ; out[i] ; i++)                                                                                                         
  88.     {                                                                                                                                      
  89.         printf("%d : %s \n",i,out[i]);                                                                                                     
  90.     }        
  91. #endif
  92. }  
  93. int execute(char* argv[])
  94. {
  95.     pid_t id = fork();
  96.     if(id < 0) return -1;                                                                                                                              
  97.     else if(id == 0)
  98.     {
  99.         int fd = 0;
  100.         if(redir == InputRedir)
  101.         {
  102.             fd = open(filename,O_RDONLY);
  103.             dup2(fd,0);
  104.         }
  105.         else if(redir == OutputRedir)
  106.         {
  107.             fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);
  108.             dup2(fd,1);
  109.         }
  110.         else if(redir == AppendRedir)
  111.         {
  112.             fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);
  113.             dup2(fd,1);
  114.         }
  115.         else
  116.         {
  117.             // do nothing
  118.         }
  119.         execvp(argv[0],argv);
  120.         // 若替换失败则子进程退出
  121.         exit(-1);
  122.     }
  123.         else
  124.     {
  125.         // sleep(1);
  126.         int status = 0;
  127.         pid_t rid = wait(&status);
  128.         if(rid>0)
  129.         {
  130.             lastcode = WEXITSTATUS(status);
  131.         }
  132.         
  133.     }
  134.     return 0;
  135. }
  136. void cd(char* path)
  137. {
  138.     chdir(path);
  139.     char tmp[1024];
  140.     // char* tmp = getenv("PWD");
  141.     getcwd(tmp,sizeof(tmp));
  142.     sprintf(cwd,"PWD=%s",tmp);
  143.     putenv(cwd);
  144. }
  145. // 内键命令并执行1,非内键命令0
  146. int dobuildin(char* argv[])
  147. {
  148.     if(strcmp(argv[0],"cd") == 0)
  149.     {
  150.         char* path = NULL;
  151.         if(argv[1] == NULL)
  152.             path = getHomename();
  153.         else
  154.             path = argv[1];
  155.         cd(path);                                                                                                                                      
  156.         return 1;
  157.     }
  158.     else if(strcmp(argv[0],"export") == 0)
  159.     {
  160.         if(argv[1] == NULL)
  161.             return 1;
  162.         else
  163.         {
  164.             strcpy(myenv[cnt],argv[1]);
  165.             putenv(myenv[cnt++]);
  166.             return 1;
  167.         }
  168.     }
  169.         else if(strcmp(argv[0],"echo") == 0)
  170.     {                                                                                                                                                  
  171.         if(argv[1] == NULL)
  172.         {
  173.             printf("\n");
  174.             return 1;
  175.         }
  176.         else if(*(argv[1]) == '$' && strlen(argv[1]) >= 2)
  177.         {
  178.             // 输出退出码
  179.             if(*(argv[1]+1) == '?')
  180.             {
  181.                 printf("%d\n",lastcode);
  182.                 lastcode = 0;
  183.             }
  184.             else  // 输出环境变量  
  185.             {
  186.                 const char* enval = getenv(argv[1]+1);
  187.                 if(enval)
  188.                 {
  189.                     printf("%s",enval);
  190.                 }
  191.                 else
  192.                 {
  193.                     printf("\n");
  194.                 }
  195.             }
  196.         }
  197.         else
  198.         {
  199.             printf("%s",argv[1]);
  200.         }
  201.         return 1;
  202.     }
  203.     else if(0){}
  204.     return 0;
  205. }
  206. // 用于跳过空格
  207. #define SkipSpace(pos) do{while(isspace(*pos)) pos++;}while(0)
  208. // 判断是否为重定向
  209. void CheckRedir(char command[])
  210. {
  211.     char* start = command;                                                                                                                             
  212.     char* end = command +  strlen(command);
  213.     while(start < end)
  214.     {
  215.         // 输入重定向
  216.         if(*end == '<')
  217.         {
  218.             *end = '\0';
  219.             filename = end + 1;
  220.             SkipSpace(filename);
  221.             redir = InputRedir;
  222.         }
  223.         else if(*end == '>')
  224.         {
  225.             // 追加重定向
  226.             if(*(end-1) == '>')
  227.             {
  228.                 *(end-1) = '\0';
  229.                 filename = end + 1;
  230.                 SkipSpace(filename);
  231.                 redir = AppendRedir;
  232.             }
  233.             // 输出重定向
  234.             else
  235.             {
  236.                 *end = '\0';
  237.                 filename = end + 1;                                                                                                                    
  238.                 SkipSpace(filename);
  239.                 redir = OutputRedir;
  240.             }
  241.         }
  242.         end--;
  243.     }
  244. }
  245. int main()                                                                                                                                 
  246. {                                                                                                                                          
  247.     while(1)                                                                                                                              
  248.     {                                                                                                                                                  
  249.         // 初始默认为非重定向
  250.         redir = NoneRedir;
  251.         filename = NULL;
  252.         char usercommand[NUM];                                                                                                            
  253.         char* argv[SIZE];   
  254.         // 打印命令提示符和获取命令行命令字符串   
  255.         int x = getUserCommand(usercommand,NUM);   
  256.    
  257.         if(x <= 0) continue;   
  258.    
  259.         // 判断是否为重定向
  260.         CheckRedir(usercommand);
  261.         // 分割命令行  "ls -a -l" -> "ls" "-a" "-l"
  262.         SplitCommand(usercommand,argv);
  263.         // 判断是否为內键命令
  264.         x = dobuildin(argv);
  265.         if(x == 1)
  266.             continue;
  267.         // 执行指令
  268.         execute(argv);
  269.     }
  270.     return 0;
  271. }
复制代码

末了

如果有什么建议和疑问,或是有什么错误,各人可以在批评区中提出。
希望各人以后也能和我一起进步!!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4