• 王晓博 2010.12.22-3-模拟linux shell 终端环境 执行所有shell命令


    作者:2008级嵌入式  王晓博

    /*-----------------------------------------------------------------------------

     注释: 模拟linux shell 终端环境  执行所有shell命令 ,命令区分空格等   
    opensource 供爱好者修改学习
    作者:王晓博  时间 10年9月 邮箱:1wangxiaobo@163.com
    [root@192 ~]#gcc -o shell shell.c
    -----------------------------------------------------------------------------*/

    /* GNU GPLv2 only or any later version. */
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
     
    #define BUFFERSIZE 80
    extern char *get_current_dir_name(void);
    extern char *getenv(const char *name);
    extern pid_t waitpid(pid_t pid, int *status, int options);

    char buffer[BUFFERSIZE + 1];

    int main()
    {
        char *path, *arg[10], *input;
        int li_inputlen, is_bj, is_back, i, j, k, pid, status;
        char lc_char;

        while (1) {
            /* initiations */
            is_bj = 0;    /*redirection flag */
            is_back = 0;    /*background */

            /* shell prompt */
            path = get_current_dir_name();
            printf("[%s>wxb]$", path);

            /*开始获取输入 */
            li_inputlen = 0;
            lc_char = getchar();
            while (lc_char != ' ') {
                if (li_inputlen < BUFFERSIZE)
                    buffer[li_inputlen++] = lc_char;
                lc_char = getchar();
            }

            /*命令超长处理*/
            if (li_inputlen >= BUFFERSIZE) {
                printf("Your command is too long! Please re-enter your command! ");
                li_inputlen = 0;    /*reset */
                continue;
            } else
                buffer[li_inputlen] = '';    /*加上串结束符号,形成字串 */

            /*将命令从缓存拷贝到input中*/
            input = (char *)malloc(sizeof(char) * (li_inputlen + 1));
            strcpy(input, buffer);

            /* 获取命令和参数并保存在arg中*/
            for (i = 0, j = 0, k = 0; i <= li_inputlen; i++) {
                /*管道和重定向单独处理 */
                if (input[i] == '<' || input[i] == '>' || input[i] == '|') {
                    if (input[i] == '|')
                        pipel(input, li_inputlen);
                    else
                        redirect(input, li_inputlen);
                    is_bj = 1;
                    break;
                }
                /*处理空格、TAB和结束符。不用处理‘ ',大家如果仔细分析前面的获取输入的程序的话,
                 *不难发现回车符并没有写入buffer*/
                if (input[i] == ' ' || input[i] == ' ' || input[i] == '') {
                    if (j == 0)    /*这个条件可以略去连在一起的多个空格或者tab */
                        continue;
                    else {
                        buffer[j++] = '';
                        arg[k] = (char *)malloc(sizeof(char) * j);
                        /*将指令或参数从缓存拷贝到arg中 */
                        strcpy(arg[k], buffer);
                        j = 0;    /*准备取下一个参数 */
                        k++;
                    }
                } else {
                    /*如果字串最后是‘&',则置后台运行标记为1 */
                    if (input[i] == '&' && input[i + 1] == '') {
                        is_back = 1;
                        continue;
                    }
                    buffer[j++] = input[i];
                }
            }

            free(input);    /*释放空间 */

            /*如果输入的指令是quit则退出while,即退出程序*/
            if (strcmp(arg[0], "quit") == 0) {
                printf("bye-bye ");
                break;
            }
            /*如果输入的指令是about则显示作者信息,同时结束本条命令的解析过程 */
            if (strcmp(arg[0], "about") == 0) {
                printf("copyright by 1wangxiaobo@163.com ");
                continue;
            }

            if (is_bj == 0) {    /*非管道、重定向指令 */
                /*在使用xxec执行命令的时候,最后的参数必须是NULL指针,
                *所以将最后一个参数置成空值*/
                arg[k] = (char *)0;
                /*判断指令arg[0]是否存在 */
                if (is_fileexist(arg[0]) == -1) {
                    printf("This command is not found?! ");
                    for (i = 0; i < k; i++)
                        free(arg[i]);
                    continue;
                }

                /* fork a sub-process to run the execution file */
                if ((pid = fork()) == 0)    /*子进程 */
                    execv(buffer, arg);
                else /*父进程 */ if (is_back == 0)    /*并非后台执行指令 */
                    waitpid(pid, &status, 0);

                /*释放申请的空间 */
                for (i = 0; i < k; i++)
                    free(arg[i]);
            }
        }
        return 0;
    }

    int is_fileexist(char *comm)
    {
        char *path, *p;
        int i;

        i = 0;
        /*使用getenv函数来获取系统环境变量,用参数PATH表示获取路径*/
        path = getenv("PATH");
        p = path;
        while (*p != '') {
            /*路径列表使用‘:’来分隔路径 */
            if (*p != ':')
                buffer[i++] = *p;
            else {
                buffer[i++] = '/';
                buffer[i] = '';
                /*将指令和路径合成,形成pathname,并使用access函数来判断该文件是否存在*/
                strcat(buffer, comm);

                if (access(buffer, F_OK) == 0)    /*文件被找到 */
                    return 0;
                else
                    /*继续寻找其它路径 */
                    i = 0;
            }
            p++;
        }
        /*搜索完所有路径,依然没有找到则返回-1*/
        return -1;
    }

    int redirect(char *in, int len)
    {
        char *argv[30], *filename[2];
        pid_t pid;
        int i, j, k, fd_in, fd_out, is_in = -1, is_out = -1, num = 0;
        int is_back = 0, status = 0;

        /*这里是重定向的命令解析过程,其中filename用于存放重定向文件,
        *is_in, is_out分别是输入重定向标记和输出重定向标记*/
        for (i = 0, j = 0, k = 0; i <= len; i++) {
            if (in[i] == ' ' || in[i] == ' ' || in[i] == '' || in[i] == '<' || in[i] == '>') {
                if (in[i] == '>' || in[i] == '<') {
                    /*重定向指令最多'<','>'各出现一次,因此num最大为2,
                    *否则认为命令输入错误*/
                    if (num < 3) {
                        num++;
                        if (in[i] == '<')
                            is_in = num - 1;
                        else
                            is_out = num - 1;

                        /*处理命令和重定向符号相连的情况,比如ls>a */
                        if (j > 0 && num == 1) {
                            buffer[j++] = '';
                            argv[k] = (char *)malloc(sizeof(char) * j);
                            strcpy(argv[k], buffer);
                            k++;
                            j = 0;
                        }
                    } else {
                        printf("The format is error! ");
                        return -1;
                    }
                }
                if (j == 0)
                    continue;
                else {
                    buffer[j++] = '';
                    /*尚未遇到重定向符号,字符串是命令或参数 */
                    if (num == 0) {
                        argv[k] = (char *)malloc(sizeof(char) * j);
                        strcpy(argv[k], buffer);
                        k++;
                    }
                    /*是重定向后符号的字符串,是文件名 */
                    else {
                        filename[status] = (char *)malloc(sizeof(char) * j);
                        strcpy(filename[status++], buffer);
                    }
                    j = 0;    /*initate */
                }
            } else {
                if (in[i] == '&' && in[i + 1] == '') {
                    is_back = 1;
                    continue;
                }
                buffer[j++] = in[i];
            }
        }

        argv[k] = (char *)0;

        if (is_fileexist(argv[0]) == -1) {
            printf("This command is not founded! ");
            for (i = 0; i < k; i++)
                free(argv[i]);
            return 0;
        }

        if ((pid = fork()) == 0) {
            /*存在输出重定向 */
            if (is_out != -1)
                if ((fd_out = open(filename[is_out], O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)) == -1) {
                    printf("Open out %s Error ", filename[is_out]);
                    return -1;
                }

            /*存在输入重定向 */
            if (is_in != -1)
                if ((fd_in = open(filename[is_in], O_RDONLY, S_IRUSR | S_IWUSR)) == -1) {
                    printf("Open in %s Error ", filename[is_out]);
                    return -1;
                }

            if (is_out != -1)
                /*使用dup2函数将标准输出重定向到fd_out上,dup2(int oldfd,int newfd)实现的
                 *是把oldfd所指的文件描述符复制到newfd。若newfd为一已打开的文件描述词,
                 *则newfd所指的文件会先被关闭,dup2复制的文件描述词与原来的文件描述词
                 *共享各种文件状态*/
                if (dup2(fd_out, STDOUT_FILENO) == -1) {
                    printf("Redirect Standard Out Error ");
                    exit(1);
                }

            if (is_in != -1)
                if (dup2(fd_in, STDIN_FILENO) == -1) {
                    printf("Redirect Standard Out Error ");
                    exit(1);
                }
            execv(buffer, argv);
        } else if (is_back == 0)    /*run on the TOP */
            waitpid(pid, &status, 0);
        for (i = 0; i < k; i++)
            free(argv[i]);

        if (is_in != -1) {
            free(filename[is_in]);
            close(fd_in);
        }
        if (is_out != -1) {
            free(filename[is_out]);
            close(fd_out);
        }
        return 0;
    }

    int pipel(char *input, int len)
    {
        char *argv[2][30];
        int i, j, k, count, is_back = 0;
        int li_comm = 0, fd[2], fpip[2];
        char lc_char, lc_end[1];
        pid_t child1, child2;

        /*管道的命令解析过程*/
        for (i = 0, j = 0, k = 0; i <= len; i++) {
            if (input[i] == ' ' || input[i] == ' ' || input[i] == '' || input[i] == '|') {
                if (input[i] == '|') {    /*管道符号 */
                    if (j > 0) {
                        buffer[j++] = '';
                        /*因为管道连接的是两个指令,所以用二维数组指针来存放命令和参数,
                         *li_comm是表示第几个指令*/
                        argv[li_comm][k] = (char *)malloc(sizeof(char) * j);
                        strcpy(argv[li_comm][k++], buffer);
                    }
                    argv[li_comm][k++] = (char *)0;
                    /*遇到管道符,第一个指令完毕,开始准备接受第二个指令 */
                    li_comm++;
                    count = k;
                    k = 0;
                    j = 0;
                }
                if (j == 0)
                    continue;
                else {
                    buffer[j++] = '';
                    argv[li_comm][k] = (char *)malloc(sizeof(char) * j);
                    strcpy(argv[li_comm][k], buffer);
                    k++;
                }
                j = 0;    /*initate */
            } else {
                if (input[i] == '&' && input[i + 1] == '') {
                    is_back = 1;
                    continue;
                }
                buffer[j++] = input[i];
            }
        }
        argv[li_comm][k++] = (char *)0;

        if (is_fileexist(argv[0][0]) == -1) {
            printf("This first command is not found! ");
            for (i = 0; i < count; i++)
                free(argv[0][i]);
            return 0;
        }
        /*指令解析结束*/

        /*建立管道*/
        if (pipe(fd) == -1) {
            printf("open pipe error! ");
            return -1;
        }

        /*创建第一个子进程执行管道符前的指令,并将输出写到管道*/
        if ((child1 = fork()) == 0) {
            /*关闭读端*/
            close(fd[0]);
            if (fd[1] != STDOUT_FILENO) {
                /*将标准输出重定向到管道的写入端,这样该子进程的输出就写入了管道 */
                if (dup2(fd[1], STDOUT_FILENO) == -1) {
                    printf("Redirect Standard Out Error ");
                    return -1;
                }
                /*关闭写入端 */
                close(fd[1]);
            }
            execv(buffer, argv[0]);
        } else {        /*父进程 */
            /*先要等待写入管道的进程结束*/
            waitpid(child1, &li_comm, 0);
            /*然后我们必须写入一个结束标记,告诉读管道进程数据到这里就完了*/
            lc_end[0] = 0x1a;
            write(fd[1], lc_end, 1);
            close(fd[1]);

            if (is_fileexist(argv[1][0]) == -1) {
                printf("This command is not founded! ");
                for (i = 0; i < k; i++)
                    free(argv[1][i]);
                return 0;
            }

            /*创建第二个进程执行管道符后的指令,并从管道读输入流 */
            if ((child2 = fork()) == 0) {
                if (fd[0] != STDIN_FILENO) {
                    /*将标准输入重定向到管道读入端 */
                    if (dup2(fd[0], STDIN_FILENO) == -1) {
                        printf("Redirect Standard In Error! ");
                        return -1;
                    }
                    close(fd[0]);
                }
                execv(buffer, argv[1]);
            } else /*父进程 */ if (is_back == 0)
                waitpid(child2, NULL, 0);
        }
        for (i = 0; i < count; i++)
            free(argv[0][i]);
        for (i = 0; i < k; i++)
            free(argv[1][i]);
        return 0;
    }

    /*-----------------------------------------------------------------------------
     注释: 模拟linux shell 终端环境  执行所有shell命令 ,命令区分空格等  
    opensource 供爱好者修改学习
    执行过程如下:
    [root@192 ~]# ./shell
    [/root>wxb]$ps
      PID TTY          TIME CMD
     5274 pts/1    00:00:00 bash
     5295 pts/1    00:00:00 shell
     5297 pts/1    00:00:00 ps
    [/root>wxb]$  ps
      PID TTY          TIME CMD
     5274 pts/1    00:00:00 bash
     5295 pts/1    00:00:00 shell
     5298 pts/1    00:00:00 ps
    [/root>wxb]$ls
    anaconda-ks.cfg   install.log         shell           tmp
    anaconda-ks.cfg~  install.log.syslog  shell1.c~       Yozo_Office
    Desktop           k -l                shell.c         zImage
    eioXpacklog.txt   minicom.log         Source Insight  我的~
    [/root>wxb]$ll
    This command is not found?!
    [/root>wxb]$  date
    2010年 12月 22日 星期三 17:35:44 CST
    [/root>wxb]$quit
    bye-bye
    [root@192 ~]#

    -----------------------------------------------------------------------------*/
       
  • 相关阅读:
    mysql学习笔记(七)
    Mysql学习笔记(八)
    vcpkg安装库
    nvm node版本管理工具
    Node.js版本管理工具nvm
    mysql安装后启动及navicat绿色版
    蚁景Web安全12期笔记
    python协程asyncio的个人理解
    EF或原生sql语句使用全文索引
    在拥挤和变化的世界中茁壮成长:C++ 2006–2020
  • 原文地址:https://www.cnblogs.com/ztguang/p/12647723.html
Copyright © 2020-2023  润新知