• UNIX-LINUX编程实践教程->第八章->实例代码注解->写一个简单的shell


    一 分析

      要实现一个shell,需包含3个步骤
      1)读入指令
      2)指令解析
      3)执行指令

    1 从键盘读入指令

      从键盘读入指令的几个要点:

      1)调用getc函数等待并获取用户键盘输入。
      2)每一行命令的结束符为' ',getsline函数就是通过这个结束符来判断用户是否完成指令的输入。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main()
    {
        char* cmdLine = (char*)malloc(sizeof(char)*100);
        char* prompt = "print your cmd >";
        int i;
    
        while(1)
        {
            
            i = NextCmd(prompt,cmdLine);
            if(i != 0)
            {
                return i;
            }
            else
            {
                printf("you print a cmd: %s 
    ",cmdLine);
            }    
        }
        free(cmdLine);
        return 0;
    }
    
    int NextCmd(char* prompt,char* cmdLine)
    {
        int i;
        printf("%s",prompt);
        i = GetsLine(cmdLine);
        if(i != 0)
        {
            return i;
        }
        else
        {
            return 0;
        }
    }
    
    int GetsLine(char* result)
    {
        int word;
    
        while(1)
        {
            word = getc(stdin);
            if(word != '
    ')
            {
                *result = word;
                result ++;
            }
            else
            {
                *result = '';
                return 0;
            }
        }
    }

     输出:

    print your cmd >asd
    you print a cmd: asd
    print your cmd >a
    you print a cmd: a
    print your cmd >

      在上面的代码中我们从键盘上获得用户的指令输入,然后再直接打印出来。在GetsLine()函数执行getc(),等待用户输入并开始记录,直到用户输入回车后返回,返回前添加''字符表示整条指令结束。在NextCmd()中输出提示符“print your cmd >”,执行GetsLine获得用户输入的整条指令并返回。最后在主函数中打印用户的输入。

    2 指令解析

      指令解析的几个要点:

      1)支持的指令格式【指令】 【参数1】 【参数2】。。。【参数n】
      2)指令与参数间,参数与参数间均以空格隔开,一条完整的指令以''结尾。
      3)因为使用函数execvp来执行指令(在后面会讲到),所以需要将解析的指令存入指针数组char* argList[]中,结尾处为NULL。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    
    int main()
    {
        char* argList[20];
        char* cmdLine = "abc -a -b -c";
        int i;
        for(i = 0;i<20;i++)
        {
            argList[i] = (char*)malloc(sizeof(char)*100);
            memset(argList[i],'',sizeof(char)*100);
        }
    
        CmdToArg(cmdLine,argList);
    
        for(i = 0;i<20;i++)
        {
            printf("get:%s
    ",argList[i]);
        }
    
        for(i = 0;i<20;i++)
        {
            free(argList[i]);
        }    
        return 0;
    
    }
    
    int CmdToArg(char* cmdLine,char* argList[20])
    {
        char aChar;
        char* pChar;
        int i = 0;
        pChar = argList[0];
        while(1)
        {
            aChar = *cmdLine;
            cmdLine++;
            if(aChar == ' ')
            {
                *pChar = '';
                i++;
                pChar = argList[i];
            }
            else if(aChar == '')
            {
                *pChar = '';
                i++;
                argList[i] = 0; 
                return 0;
            }
            else
            {
                *pChar = aChar;
                pChar++;
            }
        }
    
    }

      上述代码中,使用CmdToArg()函数,将cmdLine中的字符以空格符为间断符号解析,并存入一个argList中,且在argList的末尾处添加‘0’字符,表明一行指令的结束。

    3 执行指令

      我们使用execvp()函数来启动另一个程序,这里有几点需要注意:

      1)新的程序会覆盖原程序,导致新程序结束时原本的shell也结束了,所以需要使用fork()函数来新建一个进程来打开新的程序。
      2)fork()函数的返回值可以判断当前进程是父进程还是子进程。
      3)在父进程中使用wait()函数来等待新进程结束

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    int main()
    {
        char* arglist[3];
        int i;
    
        arglist[0] = "ls";
        arglist[1] = "-l";
        arglist[2] = 0;    
    
        i = fork();
        if(i==0)
        {
            printf("I'm the new process.ready for cmd ls
    ");
            execvp("ls",arglist);
        }
        else
        {
            wait(NULL);
            printf("I'm the old process
    ");
        }
    }    

      在上面的代码中,我们使用fork()函数创建了一个新的进程,并在新进程中使用execvp()函数来启动新的程序,并在父进程中使用wait()函数来等待子进程结束。输出如下:

    lqx@lqx-virtual-machine:~/bin/UnixPrograme/8$ ./a.out
    I'm the new process.ready for cmd ls
    total 44
    -rwxrwxr-x 1 lqx lqx 7268 2013-07-02 10:33 a.out
    -rw-rw-r-- 1 lqx lqx  497 2013-04-25 15:12 execute.c
    -rw-rw-r-- 1 lqx lqx  482 2013-04-23 15:54 psh2.c
    -rw-rw-r-- 1 lqx lqx  584 2013-04-25 15:10 smsh1.c
    -rw-rw-r-- 1 lqx lqx  202 2013-04-25 15:14 smsh.h
    -rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c
    -rw-rw-r-- 1 lqx lqx  438 2013-07-02 10:33 test.c
    -rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline
    -rw-rw-r-- 1 lqx lqx  436 2013-05-10 14:46 testline.c
    I'm the old process

    4 整合

      在前面,我们分别实现了:
      1)从用户终端获得用户的指令输入
      2)将一行指令解析为指定格式
      3)创建子进程来执行用户的指令

      现在就将以上3各部分整合到一起,实现一个基本的shell。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    
    int main()
    {
        char* cmdLine = (char*)malloc(sizeof(char)*100);
        char* prompt = "print your cmd >";
        int i;
        char* argList[20];
        for(i = 0;i<20;i++)
        {
            argList[i] = (char*)malloc(sizeof(char)*100);
            memset(argList[i],'',sizeof(char)*100);
        }
        while(1)
        {
            
            i = NextCmd(prompt,cmdLine);
            if(i != 0)
            {
                return i;
            }
            else
            {
                CmdToArg(cmdLine,argList);
            i = fork();
            if(i==0)
                {
                execvp(argList[0],argList);
            exit(0);
                }
            else
            {
            wait(NULL);
            }
            }    
        }
        free(cmdLine);
        for(i = 0;i<20;i++)
        {
            free(argList[i]);
        }  
        return 0;
    }
    
    int NextCmd(char* prompt,char* cmdLine)
    {
        int i;
        printf("%s",prompt);
        i = GetsLine(cmdLine);
        if(i != 0)
        {
            return i;
        }
        else
        {
            return 0;
        }
    }
    
    int GetsLine(char* result)
    {
        int word;
    
        while(1)
        {
            word = getc(stdin);
            if(word != '
    ')
            {
                *result = word;
                result ++;
            }
            else
            {
                *result = '';
                return 0;
            }
        }
    }
    
    int CmdToArg(char* cmdLine,char* argList[20])
    {
        char aChar;
        char* pChar;
        int i = 0;
        pChar = argList[0];
        while(1)
        {
            aChar = *cmdLine;
            cmdLine++;
            if(aChar == ' ')
            {
                *pChar = '';
                i++;
                pChar = argList[i];
            }
            else if(aChar == '')
            {
                *pChar = '';
                i++;
                argList[i] = 0; 
                return 0;
            }
            else
            {
                *pChar = aChar;
                pChar++;
            }
        }
    
    }

      测试一下:

    print your cmd >ls -l
    total 48
    -rw-rw-r-- 1 lqx lqx 1863 2013-07-02 10:52 
    -rwxrwxr-x 1 lqx lqx 7491 2013-07-02 11:10 a.out
    -rw-rw-r-- 1 lqx lqx  497 2013-04-25 15:12 execute.c
    -rw-rw-r-- 1 lqx lqx  482 2013-04-23 15:54 psh2.c
    -rw-rw-r-- 1 lqx lqx  584 2013-04-25 15:10 smsh1.c
    -rw-rw-r-- 1 lqx lqx  202 2013-04-25 15:14 smsh.h
    -rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c
    -rw-rw-r-- 1 lqx lqx 1842 2013-07-02 11:10 test.c
    -rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline
    -rw-rw-r-- 1 lqx lqx  436 2013-05-10 14:46 testline.c

      以上,我们实现了一个基本的shell,虽然还有很多不足之处,但是对shell的基本原理和功能都有了一些了解。

  • 相关阅读:
    MAC终端配置
    SCIENCE公布125个科学前沿问题
    【Python3】内存释放机制
    【Python3】装饰器
    【Python3】迭代器&装饰器
    【Python3】内置函数
    【Python3】高阶函数
    【Python3】匿名函数
    【Python3】递归函数
    【Python3】嵌套函数
  • 原文地址:https://www.cnblogs.com/cation/p/3045240.html
Copyright © 2020-2023  润新知