Chinaunix首页 | 论坛 | 博客
  • 博客访问: 231090
  • 博文数量: 51
  • 博客积分: 113
  • 博客等级: 民兵
  • 技术积分: 285
  • 用 户 组: 普通用户
  • 注册时间: 2012-03-14 17:31
文章分类

全部博文(51)

文章存档

2012年(51)

分类:

2012-05-09 18:42:22

作者: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 != '\n') {
            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!\n");
            li_inputlen = 0;    /*reset */
            continue;
        } else
            buffer[li_inputlen] = '\0';    /*加上串结束符号,形成字串 */

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

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

        /*如果输入的指令是quit则退出while,即退出程序*/
        if (strcmp(arg[0], "quit") == 0) {
            printf("bye-bye\n");
            break;
        }
        /*如果输入的指令是about则显示作者信息,同时结束本条命令的解析过程 */
        if (strcmp(arg[0], "about") == 0) {
            printf("copyright by 1wangxiaobo@163.com\n");
            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?!\n");
                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 != '\0') {
        /*路径列表使用‘:’来分隔路径 */
        if (*p != ':')
            buffer[i++] = *p;
        else {
            buffer[i++] = '/';
            buffer[i] = '\0';
            /*将指令和路径合成,形成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] == '\t' || in[i] == '\0' || 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++] = '\0';
                        argv[k] = (char *)malloc(sizeof(char) * j);
                        strcpy(argv[k], buffer);
                        k++;
                        j = 0;
                    }
                } else {
                    printf("The format is error!\n");
                    return -1;
                }
            }
            if (j == 0)
                continue;
            else {
                buffer[j++] = '\0';
                /*尚未遇到重定向符号,字符串是命令或参数 */
                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] == '\0') {
                is_back = 1;
                continue;
            }
            buffer[j++] = in[i];
        }
    }

    argv[k] = (char *)0;

    if (is_fileexist(argv[0]) == -1) {
        printf("This command is not founded!\n");
        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\n", 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\n", 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\n");
                exit(1);
            }

        if (is_in != -1)
            if (dup2(fd_in, STDIN_FILENO) == -1) {
                printf("Redirect Standard Out Error\n");
                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] == '\t' || input[i] == '\0' || input[i] == '|') {
            if (input[i] == '|') {    /*管道符号 */
                if (j > 0) {
                    buffer[j++] = '\0';
                    /*因为管道连接的是两个指令,所以用二维数组指针来存放命令和参数,
                     *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++] = '\0';
                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] == '\0') {
                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!\n");
        for (i = 0; i < count; i++)
            free(argv[0][i]);
        return 0;
    }
    /*指令解析结束*/

    /*建立管道*/
    if (pipe(fd) == -1) {
        printf("open pipe error!\n");
        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\n");
                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!\n");
            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!\n");
                    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 ~]#

-----------------------------------------------------------------------------*/
 
阅读(748) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~