• [Linux]下制作简易myshell



    前言

    本小节将基于上篇博客的内容制作一个简易的shell脚本。大家有什么不理解的可以先去了解上篇博客的内容,链接如下进程控制!!!


    正文开始!!!

    一、shell介绍

    我们先来考虑下面这个与shell典型的互动:

    在这里插入图片描述

    根据命令行shell,我们可以发现他就是一个死循环,可以让我们输入命令,来进行相应的操作。

    系统接口使用

    在这里插入图片描述
    在这次制作中,我建议大家使用这个接口,因为我们写的命令行参数容易打散为argv(指针数组)类型,方便我们去调用这个系统接口。

    二、shell的制作

    2.1 显示提示符

    int main()
    {
        while(1)
        {
            printf("[曾曾@我的主机名 当前目录]# ");
            fflush(stdout);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    在这里插入图片描述

    先来看看效果,因为我们是死循环,所以会一直打印提示符,接下来让我们获取用户的输入让程序停下来!

    2.2 获取用户输入

    #define NUM 1024
    char command_line[NUM];
    int main()
    {
        while(1)
        {
            printf("[曾曾@我的主机名 当前目录]# ");
            fflush(stdout);
    		memset(command_line,'\0',sizeof(command_line)*sizeof(char));
            fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
            command_line[strlen(command_line)-1]='\0';
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在这里插入图片描述

    现在来看,外观基本满足了我们的要求了。
    现在对主函数的内部进行封装。

    2.3 截取字符串

    现在我们基于要使用的execvp()系统调用接口,将我们输入的命令打散到指针数组中。
    即“ls -a -l”—>“ls” “-a” “-l” 截取字符串

    #define SEP " "
    #define NUM 1024
    #define SIZE 128
    char command_line[NUM];
    char* command_args[SIZE];
    int main()
    {
        while(1)
        {
            printf("[曾曾@我的主机名 当前目录]# ");
            fflush(stdout);
    		memset(command_line,'\0',sizeof(command_line)*sizeof(char));
            fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
            command_line[strlen(command_line)-1]='\0';//清空\n
            int index=1;
            while(command_args[index++]=strtok(NULL,SEP));
            for(int i=0;i<index;i++)
            {   
                printf("%d: %s\n",i,command_args[i]);
            }
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    2.4 创建子进程执行

    		pid_t id=fork();
            if(id==0)
            {
                //child
    
            }
            int status=0;
            pid_t ret=waitpid(id,&status,0);
            if(ret>0)
            {
                printf("等待子进程成功:sig:%d,code:%d\n",status&0x7F,(status>>8)&0xFF);
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.5 程序替换

    		if(id==0)
            {
                //child
                execvp(command_args[0],command_args);
                 exit(1);//执行到这里,子进程一定替换失败了
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    shell演示

    在这里插入图片描述
    现在我们就将我们自己制作的shell运行起来了!!!

    三、代码

    头文件

    #include
    #include
    #include
    #include
    #include
    #include
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    宏定义和全局数据

    #define SEP " "
    #define NUM 1024
    #define SIZE 128
    char command_line[NUM];
    char* command_args[SIZE];
    char env_buffer[NUM];//for test
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    主函数

    int main()
    {
        //shell本质上就是一个死循环
        while(1)
        {
            //不关心获取这些属性的接口,搜索一下
            //1.显示提示符
            printf("[曾曾@我的主机名 当前目录]# ");
            fflush(stdout);
            //2.获取用户输入
            memset(command_line,'\0',sizeof(command_line)*sizeof(char));
            fgets(command_line,NUM,stdin);//键盘,标准输入,stdin,获取到的是C风格的字符串,'\0'
        
            command_line[strlen(command_line)-1]='\0';
            //3.“ls -a -l”--->"ls" "-a" "-l" 截取字符串
            command_args[0]=strtok(command_line,SEP);
            
            int index=1;
            while(command_args[index++]=strtok(NULL,SEP));
           
            //5.创建进程,执行
            pid_t id=fork();
            if(id==0)
            {
                //child
                execvp(command_args[0],command_args);
                 exit(1);//执行到这里,子进程一定替换失败了
            }
            int status=0;
            pid_t ret=waitpid(id,&status,0);
            if(ret>0)
            {
                printf("等待子进程成功:sig:%d,code:%d\n",status&0x7F,(status>>8)&0xFF);
            }//end while
       }
           return 0;
    }
    
    
    • 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

    总结

    相比于系统的shell我们还有很多命令没有完成,例如内建命令和重定向,这些内容以后再带大家实现!
    (本章完!)

  • 相关阅读:
    [每周一更]-(第67期):docker-compose 部署php的laravel项目
    vue3+vite中使用vuex
    职业成功指南:10条核心原则(上)丨三叠云
    使用redis,怎么解决并发问题?
    GAN论文阅读笔记(9)—— Dual-path Image Inpainting with Auxiliary GAN Inversion
    SpringMVC Day 03 : 处理静态资源
    深度学习入门(五十二)计算机视觉——风格迁移
    【JavaSE】多态
    百度校园招聘历年经典面试题汇总:开发测试岗
    Map和Set
  • 原文地址:https://blog.csdn.net/m0_61560468/article/details/127603050