• 【Linux】shell命令行简单解释器



    回顾一下,我们前面学习了进程创建,进程终止,进程等待,进程替换,通过这些内容我们可以来进行实现简单的shell命令行解释器!!!下面我们直接来看一看如何去实现shell命令行解释器:

    总体分为(整体需要循环哦):

    1.输出提示符

    2.输入和获取命令

    3.fork创建子进程

    4.内建命令


    • 输出提示符

    这里的提示字符为用户名@主机名 当前路径# 直接打印出来作为提示所用,也可以自己设置成其他的,问题不大

    printf("用户名@主机名 当前路径# ");
    
    • 1

    同时,这里并没有\n,会有缓冲区的问题,类似于我们之前所说的进度条所遇到的问题,在这个地方哦可以用fflush(stdout)刷新缓冲区

    • 输入和获取命令

    输入

    我们需要获取一行的内容,利用fgets函数获取,同时,可以定义一个lineCommand[NUM]数组

    char*s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
    assert(s != NULL);
    
    • 1
    • 2

    但是打印的时候却多换了一行,这是我们把\n也读取到了,直接进行处理即可,清除最后一个\n

    lineCommand[strlen(lineCommand)-1] = 0;       
    
    • 1

    获取

    输入之后,我们自然需要去进行获取,我们需要分割命令行,这个地方用strtok。把字符串切割成若干个子串:

    strtok:第一次直接传递参数,第二次则必须传NULL。且在最终strtok会返回NULL。

    • fork创建进程

    利用fork创建子进程,同时父进程需要等待子进程退出返回结果

    另外我们还需要选择替换函数execvp:首先替换函数需要先带上v,可将所有的执行参数放入数组中统一传递,其次还要选择带上p,我们输入的只有程序命令,带上p会自动在环境变量中寻找

    至此,基本的框架我们已经搞定了。

    • shell运行原理

    同时,在理解一下shell的运行原理:shell内部提取命令行做分析,然后调用exec. shell执行命令必须通过创建子进程,如果不创建子进程会把我们所有的shell全部替换,所以执行命令时一般磁盘上的程序必须创建子进程

    • 内建命令

    我们在运行自己写的shell的时候,发现输入cd …输入cd path等命令时发现路径并没有改变!

    image-20221130111814277

    没有发生改变是因为自己写的shell执行很多命令都要fork()创建子进程,让子进程执行的cd,子进程有自己的工作目录,所以更改的子进程的目录,子进程执行完毕,继续用的是父进程,既shell,并没有影响父进程,所以并没有改变。

    对于cd,我们可以采用内建命令:不需要创建子进程执行,让shell自己执行命令,称为内建命令。本质就是执行系统接口,我们可以调用一个系统接口chdir,可解决上述问题:

    image-20221130142857416

    image-20221130142956990

    简易shell——代码实现

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define NUM 1024
    #define OPT_NUM 64
    char lineCommand[NUM];
    char *myargv[OPT_NUM];
    int lastCode = 0;
    int lastSig = 0;
    int main()
    {
        while(1)
        {
            //输出提示符
            printf("用户名@主机名 当前路径#");
            fflush(stdout);
            //获取输入
            char*s = fgets(lineCommand,sizeof(lineCommand)-1,stdin);
            assert(s != NULL);
            (void) s;
            lineCommand[strlen(lineCommand)-1] = 0;
            // printf("test:%s\n",lineCommand);
            //ls -a -l -i  字符串切割
            myargv[0] = strtok(lineCommand," ");
            int i = 1;
            if(myargv[0]!= NULL&& strcmp(myargv[0],"ls") == 0)
            {
                myargv[i++] =(char*)"--color=auto";
            }
            //如果没有子串,strtok会返回NULL                                                                   
            while(myargv[i++] = strtok(NULL," "));
            //如果是cd命令, 不需要创建子进程,让shell自己执行对应的命令,本质就是执行系统接口
            //像这种不需要我们的子进程来执行,而是让shell自己执行的命令 --内建 内置命令
            if(myargv[0]!=NULL&& strcmp(myargv[0],"cd")==0)
            {
                if(myargv[1] != NULL) chdir(myargv[1]);
                continue;
            }
            if(myargv[0]!=NULL&& myargv[1]!=NULL && strcmp(myargv[0],"echo")== 0)
            {
                if(strcmp(myargv[1],"$?") == 0)                                                              
                {
                    printf("%d %d\n",lastCode,lastSig);
                }
                else 
                {
                    printf("%s\n",myargv[1]);
                }
                  continue;
            }
            //利用条件编译测试代码是否成功
    #ifdef DEBUG 
            for(int i = 0;myargv[i];i++)
            {
                printf("myargv[%d]:%s\n",i,myargv[i]);
            }
    #endif
            //执行命令
            pid_t id = fork();
            assert(id!=-1);
            if(id == 0)
            {
                execvp(myargv[0],myargv);
                exit(1);
            }
            int status = 0;
            pid_t ret = waitpid(id,&status,0);
            assert(ret > 0);
            (void)ret;
            lastCode = ((status>>8)&0xFF);
            lastSig = (status & 0X7F);
        }
        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
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78

    image-20221130152756486

    image-20221130153006990

  • 相关阅读:
    阿里云物联网平台
    Pytorch:模块(Module类)
    centos7.9脚本,怎么排除特定的访问记录
    [车联网安全自学篇] 二. IoT安全之树莓派4B/2W,烧写SD卡刷入Android 系统
    DETR:End-to-End Object Detection with Transformers
    【剑指 Offer 14- II. 剪绳子 II】(剪绳子I大数)
    深度图(Depth Map)
    ZCMU--5230: 排练方阵(C语言)
    基于云边协同架构的五大应用场景革新
    【Rust】——【面向对象语言的特征】
  • 原文地址:https://blog.csdn.net/weixin_60478154/article/details/128117711