• Linux ——shell的模拟实现


    用户和操作系统如何沟通交流,操作系统是不信任用户的,所以提供了一系列的系统接口让用户去调用。shell也是一个应用程序,它提供了界面,并且用户可以输入指令就能够和操作系统打交道。本文,实现一个简单的shell,当然我们不实现提供界面呀的一些复杂操作。我们只要求,输入指令,然后执行指令这样的功能。


    (1) 我们知道shell执行完一个指令后,会立马刷新出,然后还能输入指令,所以我们先实现一个不断循环输出的my_shell。

    #include
    int main()
    {
      while(1)
      {
        // 打印提示符
        printf("[who@myhostname mydir]#");
        fflush(stdout);    
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    (2) 获取指令
    指令是一个字符串,我们默认其长度为128,这个可以利用宏定义,也方便以后修改。利用fgets(),可以获取指令,注意我们获取的指令中含有’\n’,所以我们要去掉末尾的’\n’。

    #include
    #include
    #include
    #include
    #define NUM 128
    int main()
    {
      char command[NUM];
      while(1)
      {
        //清理s中的数据
        command[0]=0;
        //打印提示框
        printf("[who@myhostname mydir]#");
        fflush(stdout);
        //获取指令字符串
        fgets(command,NUM,stdin);
        command[strlen(command)-1]='\0';
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    (3) 解析指令
    我们获取到了指令,还得解析指令,我们必须拿到这个字符串数组中的每一个字符串。
    比如: ls -a -l,存放到了字符串数组中,我们需要把每个字符串,都单拎出来。变成:‘ls’,‘-a’,‘-l’。
    所以会用到strtok()这个字符串分解函数,命令行上字符串是以’ '分开的。分解命令字符串后,保存到一个新的指针数组中。指针数组的大小可以定义宏为64。

    #include
    #include
    #include
    #include
    #define NUM 128
    #define comd_NUM 64
    int main()
    {
      char command[NUM];
      while(1)
      {
        char * arv[comd_NUM]={NULL};
        s[0]=0;
        // 
        printf("[who@myhostname mydir]#");
        fflush(stdout);
        //
        fgets(command,NUM,stdin);
        command[strlen(command)-1]='\0';
        //分解字符串,保存到arv中
        const char* tal=" ";
        arv[0]=strtok(command,tal);
        int i=1;
        while(arv[i]=strtok(NULL,tal))
        {
           i++;
        }
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    (4) 执行指令
    指令我们已经取出来了,接下来就应该是执行命令,我们创建一个子进程去执行指令,父进程等待子进程,拿出退出信息。这里用到了进程替换以及进程等待,不懂得小伙伴,可以看我之前写的博客。

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define NUM 128
    #define CMD_NUM 64
    
    int main()
    {
        char command[NUM];
        while(1)
        {
            char *argv[CMD_NUM] = { NULL };
            //1. 打印提示符
            command[0] = 0; //用这种方式,可以做到O(1)时间复杂度,清空字符串
            printf("[who@myhostname mydir]# ");
            fflush(stdout);
            //2. 获取命令字符串
            fgets(command, NUM, stdin);
            command[strlen(command) - 1] = '\0'; //"ls\n\0"
            //printf("echo: %s\n", command);
    
            //"ls -a -b -c\0";
            //3. 解析命令字符串, char *argv[];
            //strtok();
            const char *sep = " ";
            argv[0] = strtok(command, sep);
            int i = 1;
            while(argv[i] = strtok(NULL, sep)){
                i++;
            }
            }
    
            //4. 执行第三方命令
            if(fork() == 0){
                //child
                execvp(argv[0], argv);
                exit(1);
            }
    
            int status = 0;
            waitpid(-1, &status, 0);
            printf("exit code: %d\n", (status >> 8)&0xFF);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    现在我们来看看运行效果:
    在这里插入图片描述
    但是还有一个问题没有解决:
    我们来运行一些 cd指令。
    在这里插入图片描述
    可以看到,路径并没有回退,这是什么原因?我们得指令一直用的是子进程来执行,回退路径应该是父进程去执行,需要父进程执行的命令为内键命令,比如:cd。
    所以我们需要在加一个判断,是否为”cd“指令。

    #include 
    #include 
    #include 
    #include 
    #include 
    
    #define NUM 128
    #define CMD_NUM 64
    
    int main()
    {
        char command[NUM];
        for( ; ; ){
            char *argv[CMD_NUM] = { NULL };
            //1. 打印提示符
            command[0] = 0; //用这种方式,可以做到O(1)时间复杂度,清空字符串
            printf("[who@myhostname mydir]# ");
            fflush(stdout);
            //2. 获取命令字符串
            fgets(command, NUM, stdin);
            command[strlen(command) - 1] = '\0'; //"ls\n\0"
            //printf("echo: %s\n", command);
    
            //"ls -a -b -c\0";
            //3. 解析命令字符串, char *argv[];
            //strtok();
            const char *sep = " ";
            argv[0] = strtok(command, sep);
            int i = 1;
            while(argv[i] = strtok(NULL, sep)){
                i++;
            }
    
            //4.检测命令是否是需要shell本身执行的,内建命令
               if(strcmp(argv[0], "cd") == 0){
               if(argv[1] != NULL) 
               chdir(argv[1]);
                continue;
            }
    
            //5. 执行第三方命令
            if(fork() == 0){
                //child
                execvp(argv[0], argv);
                exit(1);
            }
    
            int status = 0;
            waitpid(-1, &status, 0);
            printf("exit code: %d\n", (status >> 8)&0xFF);
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    chdir(),是更改路径的函数。


    以上就是我们shell的模拟实现

  • 相关阅读:
    AtCoder 265G 线段树
    Jmeter接口测试和性能测试
    挺进欧洲:中国汽车如何破解品牌与成本双重困境?
    JAVA题库——关于java中的方法
    4、linux下文件的通用操作方法
    多项式算法——快速数论变换NTT
    不断优化的素数算法
    【PAT甲级】1027 Colors in Mars
    .Net分表分库动态化处理
    JavaScript常用浏览器对象
  • 原文地址:https://blog.csdn.net/lyzzs222/article/details/126766411