简单模拟shell解释器

举报
跳动的bit 发表于 2022/08/30 08:02:53 2022/08/30
【摘要】 💦 简单模拟shell解释器子进程执行新程序的需求 ❓  在之前,我们举过 1 个例子:你是村长家的儿子,是程序员,你不擅长和女生打交道,所以你去通过王婆去找如花表达你的爱意,村里人都知道如花已经心有所属了,而你又是村长家的儿子。王婆心想,这趟浑水我可不不趟,万一搞砸了,可能会影响到自己以后的职业发展,但又碍于你是村长家的儿子,不敢得罪。所以,机智的王婆说:呀!最近的活太多了,这样吧,我...
💦 简单模拟shell解释器

子进程执行新程序的需求 ❓

在这里插入图片描述

  在之前,我们举过 1 个例子:你是村长家的儿子,是程序员,你不擅长和女生打交道,所以你去通过王婆去找如花表达你的爱意,村里人都知道如花已经心有所属了,而你又是村长家的儿子。王婆心想,这趟浑水我可不不趟,万一搞砸了,可能会影响到自己以后的职业发展,但又碍于你是村长家的儿子,不敢得罪。所以,机智的王婆说:呀!最近的活太多了,这样吧,我给你找我们公司的销冠(其实是实习生,比较好欺负),你自己跟销冠对接。就算谈不成,王婆也可以周旋(再给你找业务好的实习生)。王婆会根据工作的难易程度,简单的自己做,复杂的交给其它人。

  这里你是用户;王婆是命令行解释器中的 bash;销冠(实习生)是子进程;如花是操作系统;销冠(实习生)谈砸了,不影响王婆就如子进程崩了不会影响父进程。

✔ 测试用例一:

#include<stdio.h>                                                                                                                      
#include<stdlib.h>     
#include<string.h>                             
#include<unistd.h>
#include<sys/types.h>                                                 
#include<sys/wait.h>
                         
#define NUM 128           
#define SIZE 32                                                                                                                        
                    
char command_line[NUM];
char* command_parse[SIZE];
                                                         
int main()                                      
{                
    while(1)              
    {            
        //1.获取命令
        memset(command_line, '\0', sizeof(command_line));
        printf("[DanceBit@myhost myshell]$ ");
        fflush(stdout);    
        if(fgets(command_line, NUM - 1, stdin))
        {              
            command_line[strlen(command_line) - 1] = '\0';//\n = '\0'    
                                                          
            //2.加工命令 
            int index = 0;
            command_parse[index] = strtok(command_line, " ");//strtok以空格分割"ls -a -l"于指针数组,strtok可以适用于"ls   -a      -l"。
            while(1)
            {  
                index++;
                command_parse[index] = strtok(NULL, " "); 
                if(command_parse[index] == NULL)
                {         
                    break;
                }
            }
            
            //3.执行非内置命令
            if(fork() == 0)
            {                                            
                //child                       
                execvp(command_parse[0], command_parse);//我们选择v、p。
                exit(1);//父进程拿到1,说明execvp替换失败。
            }
            //father                                                  
            int status = 0;
            pid_t ret = waitpid(-1, &status, 0);
            if(ret > 0 && WIFEXITED(status))
            {                                                          
                printf("Exit Code: %d\n", WEXITSTATUS(status));
            }
        }                                                             
    }                                           
    return 0;    
}                                                                      

💨运行结果:

请添加图片描述

  可以看到我们自己模拟的 shell 可以支持大部分命令,但有部分命令是不支持的,如 ll、>、| 。不支持的原因也很好理解,重定向和管道是需要我们理解了它的原理,然后才能实现的,后面我们再对 myshell 进行完善。

myshell 和 mini_shell 中使用的 echo 是同一个 echo 吗 ❓

在这里插入图片描述

  不一定,如果感觉有些抽象的话,可以这么理解:有些命令实际让子进程去运行,子进程是不影响父进程的,此时有可能就会出现一些奇怪的现象,如。

在这里插入图片描述

为什么 ???

  1. ps ajx | head -1 && ps ajx | grep mini_shell查看 mini_shell 进程信息。

    在这里插入图片描述

  2. ls /proc/20339 -al查看 mini_shell 20339 进程相关资源。

    在这里插入图片描述

      其中 cwd 是当前所处的目录,exe 是执行程序的名字。我们发现在 myshell 中cd ../../../执行是成功的,但路径并没有任何变化。

    这里的 cd 更改的是父进程所在路径还是子进程所在路径 ??

      我们需要明确一件事,在 cd.. 时,是期望更改子进程还是父进程的路径呢。子进程是目标程序,父进程是 shell。实际上我们想改的并不是子进程,因为子进程的路径一改,子进程就退出了,改就没有意义了。所以我们要改的是父进程的路径,换言之,你要改父进程的路径的前提是不能创建子进程执行 cd。父进程也不能执行 cd,因为父进程一旦替换就会把父进程的代码替换成 cd 的代码,父进程不仅越俎代庖,自己的本职工作也没做好。

      其实虽然 cd 像命令,但实际上在 shell 中就压根不能使用程序替换执行,它使用系统接口来完成 “ 命令的执行 ”,这个接口是 chdir。

    #include<unistd.h>
    
    int chdir(const char* path);
    

✔ 测试用例二:

支持 cd。

#include<stdio.h>                                                                                                                      
#include<stdlib.h>     
#include<string.h>                             
#include<unistd.h>
#include<sys/types.h>                                                 
#include<sys/wait.h>
                         
#define NUM 128           
#define SIZE 32                                                                                                                        
                    
char command_line[NUM];
char* command_parse[SIZE];
                                                         
int main()                                      
{                
    while(1)
    {
        //1.获取命令
        memset(command_line, '\0', sizeof(command_line));
        printf("[DanceBit@myhost myshell]$ ");
        fflush(stdout);
        if(fgets(command_line, NUM - 1, stdin))
        {
            command_line[strlen(command_line) - 1] = '\0';//\n = '\0'    

            //2.加工命令 
            int index = 0;
            command_parse[index] = strtok(command_line, " ");//strtok以空格分割"ls -a -l"于指针数组,strtok可以适用于"ls   -a      -l"。
            while(1)
            {
                index++;
                command_parse[index] = strtok(NULL, " ");
                if(command_parse[index] == NULL)
                {
                    break;
                }
            }

            //3.执行第三方命令(内置命令)
            if(strcmp(command_parse[0], "cd") == 0 && chdir(command_parse[1]) == 0)
            {
                continue;
            }

            //4.执行非内置命令
            if(fork() == 0)
            {
                //child                       
                execvp(command_parse[0], command_parse);//我们选择v、p。
                exit(1);//父进程拿到1,说明execvp替换失败。
            }
            //father                                                  
            int status = 0;
            pid_t ret = waitpid(-1, &status, 0);
            if(ret > 0 && WIFEXITED(status))
            {
                printf("Exit Code: %d\n", WEXITSTATUS(status));
            }
        }
    }
    return 0;
}

💨运行结果:

在这里插入图片描述

  可以看到这样的 cd 命令并没有创建子进程去执行,本质 cd 是内置命令,它是 shell 内的一个函数调用。所以这里简单的工作,shell 自己做,复杂的就交给子进程做。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。