操作系统实验四:Shell的实现

  一、实验内容

实现具有管道、重定向功能的shell

能够执行一些简单的基本命令,如进程执行、列目录等。

 

二、实验目的

1.学习并理解linux中shell的知识;

2.重点学会编程实现管道和重定向的功能;

3.实现自己的shell

 

三、设计思路和流程图

1.对输入的命令进行解析

实验内容主要是管道和重定向,这两个功能涉及shell“|”和“<”以及“>”等不同符号,所以要对输入的命令进行解析。初步按照空格来分,之后再按照<、>、|这些涉及管道和重定向的符号来分。

2.简单命令的执行

使用函数execvp可以实现简单的命令,这些命令暂时不涉及管道和重定向,函数原型为int execvp(const char *file ,char * const argv []);,execvp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名,找到后便执行该文件,然后将第二个参数argv传给该欲执行的文件。为了不造成阻塞,这里启用了一个新线程实现它,最后父进程需等待子进程,以回收分配给它的资源。下面有些地方也用到这种方法。

3.输入输出重定向的实现

实现重定向的主要函数是freopen,FILE *freopen( const char *path, const char *mode, FILE *stream );path: 文件名,用于存储输入输出的自定义文件名。 mode: 文件打开的模式。和fopen中的模式(如r-只读, w-写)相同。 stream: 一个文件,通常使用标准流文件。函数实现重定向,把预定义的标准流文件定向到由path指定的文件中。要注意的是第二个参数,刚开始我是用的a+,结果每次输出都加到文件末尾。后来查了一下,改成w+可以先清空再写入文件。

4.管道功能的实现

命令之间通过|符号来分隔,使用pipe函数来建立管道。如何分隔这些命令呢?上面是写一个函数通过空格来分离每个字符串,这里通过strtok_r函数来分隔命令。利用pipe函数生成的的读取端和写入端,第一条命令的输出作为第二条命令的输入,从而实现管道的功能。

 

四、主要数据结构及说明

主要使用了数组和指针,存放相关的命令,通过字符串操作实现一些基本的逻辑。

 

五、源程序并附上注释

上面所说的设计思路与程序中的函数是对应的。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include<signal.h>
#include <fcntl.h>
#define hist_size 1024 
char *hist[hist_size];
int f = 0;   //to save change in directory
int head = 0, filled = 0;


//1-parse user's input
void parse(char *word, char **argv)
{
    int count = 0;
    memset(argv, 0, sizeof(char*)*(64));//copies 0 to the first 64 characters of the string pointed to by the argument argv.
    char *lefts = NULL;
    const char *split = " ";  //setting delimeter
    while (1)
    {
        char *p = strtok_r(word, split, &lefts);//ref-"strtok-r":http://baike.baidu.com/view/6991509.htm?fr=aladdin
        if (p == NULL)
        {
            break;
        }
        argv[count] = p;//argv is an array, it stores each value of your input divided by " "
        word = lefts;
        //printf("%s\n",argv[count]);
        //printf("%s\n",word);
        count++;
    }
    if (strcmp(argv[0], "exit") == 0)
        exit(0);
    else if (strcmp(argv[0], "cd") == 0)
    {
        int ch = chdir(argv[1]); //ref-"chdir":http://baike.baidu.com/view/653970.htm?fr=aladdin
        /*if(ch<0)
        {
        printf("No such file or directory %d\n.",ch);
        }*/
        f = 1;
    }
}


//2-get the first word
char *trim(char *string)//remove extra spaces
{
    int i = 0;
    int j = 0;
    char *ptr = malloc(sizeof(char*)*strlen(string));
    for (i = 0; string[i] != '\0'; i++)
    if (string[i] != ' ')
    {
        ptr[j] = string[i];
        j++;
    }
    ptr[j] = '\0';
    string = ptr;
    //printf("%s\n",string);
    //printf("%d\n",j);
    return string;
}


//3-execute the basic order
void execute(char **argv)
{
    pid_t pid;
    int status;
    //fork child process
    if ((pid = fork()) < 0)
    {
        printf("error:fork failed.\n");
        exit(1);
    }
    else if (pid == 0)
    {
        if (execvp(argv[0], argv) < 0 && strcmp(argv[0], "cd"))//ref-"execvp":http://baike.baidu.com/view/1745420.htm?fr=aladdin
            printf("error:invalid command.\n");
        exit(0);
    }
    else
    {
        while (wait(&status) != pid)//ref-"wait":http://see.xidian.edu.cn/cpp/html/289.html
            ;
    }
}


//4-output redirect
//output:file in which we need to print the output
void  execute_file(char **argv, char *output)
{
    //printf("output:%s\n",output);
    //printf("argv:%s\n",*argv);
    pid_t pid;
    int status, flag;
    char *file = NULL;
    if ((pid = fork()) < 0)
    {
        printf("error:fork failed.\n");
        exit(1);//fclose(fd1);
    }
    else if (pid == 0)
    {
        if (strstr(output, ">")>0)//returns a pointer to first occurence after > or null
        {
            char *p = strtok_r(output, ">", &file);
            output += 1;   //change2
            file = trim(file);//get the first word of file, that is to say, get the first word after >
            flag = 1;
            //printf("file : %s output : %s \n",*argv,output);
            int old_stdout = dup(1);
            //printf("mark\n");
            //output redirect
            FILE *fp1 = freopen(output, "w+", stdout);//ref-"freopen":http://baike.baidu.com/view/656692.htm?fr=aladdin
            //printf("mark\n");
            execute_file(argv, file);
            fclose(stdout);
            FILE *fp2 = fdopen(old_stdout, "w");//ref-"fdopen":http://baike.baidu.com/view/656646.htm?fr=aladdin
            *stdout = *fp2;
            exit(0);
        }
        if (strstr(output, "<") > 0)
        {
            char *p = strtok_r(output, "<", &file);
            file = trim(file);
            flag = 1;
            int fd = open(file, O_RDONLY);//ref-"open":http://see.xidian.edu.cn/cpp/html/238.html
            if (fd<0)
            {
                printf("No such file or directory.");
                exit(0);
            }
        }
        if (strstr(output, "|")>0)
        {
            fflush(stdout); printf("here"); fflush(stdout);
            char *p = strtok_r(output, "|", &file);
            file = trim(file);
            flag = 1;
            //fflush(stdout);printf("%s",file);fflush(stdout);
            char *args[64];
            parse(file, args);
            execute(args);
        }
        int old_stdout = dup(1);
        FILE *fp1 = freopen(output, "w+", stdout);
        if (execvp(argv[0], argv) < 0)
            printf("error:in exec");
        fclose(stdout);
        FILE *fp2 = fdopen(old_stdout, "w");
        *stdout = *fp2;
        exit(0);
    }
    else
    {
        while (wait(&status) != pid)
            ;
    }
}


//5-input redirect
void  execute_input(char **argv, char *output)
{
    pid_t pid;
    int fd;
    char *file;
    int flag = 0;
    int status;
    if ((pid = fork()) < 0)
    {
        printf("error:fork failed\n");
        exit(1);
    }
    else if (pid == 0)
    {
        if (strstr(output, "<")>0)
        {
            char *p = strtok_r(output, "<", &file);
            file = trim(file);
            flag = 1;
            //printf("file : %s output : %s \n",file,output);
            fd = open(output, O_RDONLY);
            if (fd<0)
            {
                printf("No such file or directory.");
                exit(0);
            }
            output = file;
        }
        if (strstr(output, ">")>0)
        {
            char *p = strtok_r(output, ">", &file);
            file = trim(file);
            flag = 1;
            fflush(stdout);
            //printf("file : %s output : %s \n",file,output);
            fflush(stdout);
            int old_stdout = dup(1);
            FILE *fp1 = freopen(file, "w+", stdout);
            execute_input(argv, output);
            fclose(stdout);
            FILE *fp2 = fdopen(old_stdout, "w");
            *stdout = *fp2;
            exit(0);
        }
        if (strstr(output, "|") > 0)
        {
            char *p = strtok_r(output, "|", &file);
            file = trim(file);
            flag = 1;
            char *args[64];
            parse(file, args);
            int pfds[2];
            pid_t pid, pid2;
            int status, status2;
            pipe(pfds);
            int fl = 0;
            if ((pid = fork()) < 0)
            {
                printf("error:fork failed\n");
                exit(1);
            }
            if ((pid2 = fork()) < 0)
            {
                printf("error:fork failed\n");
                exit(1);
            }
            if (pid == 0 && pid2 != 0)
            {
                close(1);
                dup(pfds[1]);
                close(pfds[0]);
                close(pfds[1]);
                fd = open(output, O_RDONLY);
                close(0);
                dup(fd);
                if (execvp(argv[0], argv) < 0)
                {
                    close(pfds[0]);
                    close(pfds[1]);
                    printf("error:in exec");
                    fl = 1;
                    exit(0);
                }
                close(fd);
                exit(0);
            }
            else if (pid2 == 0 && pid != 0 && fl != 1)
            {
                close(0);
                dup(pfds[0]);
                close(pfds[1]);
                close(pfds[0]);
                if (execvp(args[0], args) < 0)
                {
                    close(pfds[0]);
                    close(pfds[1]);
                    printf("error:in exec");
                    exit(0);
                }
            }
            else
            {
                close(pfds[0]);
                close(pfds[1]);
                while (wait(&status) != pid);
                while (wait(&status2) != pid2);
            }
            exit(0);
        }
        fd = open(output, O_RDONLY);
        close(0);
        dup(fd);
        if (execvp(argv[0], argv) < 0)
        {
            printf("error:in exec");
        }
        close(fd);
        exit(0);
    }
    else
    {
        while (wait(&status) != pid);
    }

}



//6-implement pipe
void execute_pipe(char **argv, char *output)
{
    int pfds[2], pf[2], flag;
    char *file;
    pid_t pid, pid2, pid3;
    int status, status2, old_stdout;
    pipe(pfds);//create pipe
    //pfds[0]:read        pfds[1]:write
    int blah = 0;
    char *args[64];
    char *argp[64];
    int fl = 0;
    if ((pid = fork()) < 0)
    {
        printf("error:fork failed\n");
        exit(1);
    }
    if ((pid2 = fork()) < 0)
    {
        printf("error:fork failed\n");
        exit(1);
    }
    if (pid == 0 && pid2 != 0)
    {
        close(1);
        dup(pfds[1]);
        close(pfds[0]);
        close(pfds[1]);
        if (execvp(argv[0], argv) < 0)//run the command
        {
            close(pfds[0]);
            close(pfds[1]);
            printf("error:in exec");
            fl = 1;
            kill(pid2, SIGUSR1);
            exit(0);
        }
    }
    else if (pid2 == 0 && pid != 0)
    {
        if (fl == 1){ exit(0); }
        if (strstr(output, "<") > 0)
        {
            char *p = strtok_r(output, "<", &file);
            file = trim(file);
            flag = 1;
            parse(output, args);//divide output to the array args
            execute_input(args, file);
            close(pfds[0]);
            close(pfds[1]);
            exit(0);
        }
        if (strstr(output, ">") > 0)
        {
            char *p = strtok_r(output, ">", &file);
            file = trim(file);
            flag = 1;
            //fflush(stdout);printf("file : %s output : %s \n",file,output);fflush(stdout);
            parse(output, args);
            blah = 1;
        }

        else
        {
            parse(output, args);
        }
        close(0);
        dup(pfds[0]);
        close(pfds[1]);
        close(pfds[0]);
        if (blah == 1)
        {
            old_stdout = dup(1);
            FILE *fp1 = freopen(file, "w+", stdout);
        }
        if (execvp(args[0], args) < 0)
        {
            fflush(stdout);
            printf("error:in exec %d", pid);
            kill(pid, SIGUSR1);
            close(pfds[0]);
            close(pfds[1]);
        }
        fflush(stdout);
        printf("HERE");
        //kill (pid, SIGUSR1);
        if (blah == 1)
        {
            fclose(stdout);
            FILE *fp2 = fdopen(old_stdout, "w");
            *stdout = *fp2;
        }
    }
    else
    {
        close(pfds[0]);
        close(pfds[1]);
        while (wait(&status) != pid);
        while (wait(&status2) != pid2);
    }
}



//7-implement pipe
void execute_pipe2(char **argv, char **args, char **argp)
{
    int status;
    int i;
    int pipes[4];
    pipe(pipes);
    pipe(pipes + 2);
    if (fork() == 0)
    {
        dup2(pipes[1], 1);
        close(pipes[0]);
        close(pipes[1]);
        close(pipes[2]);
        close(pipes[3]);
        if (execvp(argv[0], argv) < 0)
        {
            fflush(stdout);
            printf("error:in exec");
            fflush(stdout);
            close(pipes[0]);
            close(pipes[1]);
            close(pipes[2]);
            close(pipes[3]);
            exit(1);
        }
    }
    else
    {
        if (fork() == 0)
        {
            dup2(pipes[0], 0);
            dup2(pipes[3], 1);
            close(pipes[0]);
            close(pipes[1]);
            close(pipes[2]);
            close(pipes[3]);
            if (execvp(args[0], args) < 0)
            {
                fflush(stdout);
                printf("error:in exec");
                fflush(stdout);
                close(pipes[0]);
                close(pipes[1]);
                close(pipes[2]);
                close(pipes[3]);
                exit(1);
            }
        }
        else
        {
            if (fork() == 0)
            {
                dup2(pipes[2], 0);
                close(pipes[0]);
                close(pipes[1]);
                close(pipes[2]);
                close(pipes[3]);
                if (execvp(argp[0], argp) < 0)
                {
                    fflush(stdout);
                    printf("error:in exec");
                    fflush(stdout);
                    close(pipes[0]);
                    close(pipes[1]);
                    close(pipes[2]);
                    close(pipes[3]);
                    exit(1);
                }
            }
        }
    }
    close(pipes[0]);
    close(pipes[1]);
    close(pipes[2]);
    close(pipes[3]);
    for (i = 0; i < 3; i++)
        wait(&status);
}

/*
int main()
{
char word[1024]="ls>a.txt";
char *argv[1024];
char *file=NULL;
char *p=strtok_r(word ,">", &file);
file=trim(file);
parse(word,argv);
execute_file(argv,file);
}*/



int  main()
{
    char line[1024];
    char *argv[64];
    char *args[64];
    char *left;
    size_t size = 0;
    char ch;
    int count = 0;
    char *tri;
    char *second;
    char *file;
    int i;
    for (i = 0; i < hist_size; i++)
    {
        hist[i] = (char *)malloc(150);
    }

    while (1)
    {
        count = 0;
        int flag = 0;
        char *word = NULL;
        char *dire[] = { "pwd" };
        fflush(stdout);
        printf("SHELL~");
        fflush(stdout);
        execute(dire); //print the current directory, we can also use getcwd()
        printf("$");
        int len = getline(&word, &size, stdin);
        if (*word == '\n')
            continue;
        word[len - 1] = '\0';
        char *file = NULL;
        int i = 0;
        char *temp = (char *)malloc(150);
        strcpy(temp, word);
        parse(temp, argv);

        strcpy(hist[(head + 1) % hist_size], word);   //storing an entry in history
        head = (head + 1) % hist_size;
        filled = filled + 1;

        for (i = 0; word[i] != '\0'; i++)
        {

            if (word[i] == '>')
            {
                //printf("%s\n",word);   //has the initial command
                char *p = strtok_r(word, ">", &file);
                file = trim(file);
                //printf("%s\n",file); 
                flag = 1;
                break;
            }
            else if (word[i] == '<')
            {
                char *p = strtok_r(word, "<", &file);
                file = trim(file);
                //printf("file : %s",file);
                flag = 2;
                break;
            }
            else if (word[i] == '|')
            {
                char *p = strtok_r(word, "|", &left);
                flag = 3;
                break;
            }
        }
        if (strcmp(word, "exit") == 0)
        {
            exit(0);
        }

        if (flag == 1)
        {
            parse(word, argv);   //parsed command stored in argv
            //printf("At the right place");
            execute_file(argv, file);
        }
        else if (flag == 2)
        {
            parse(word, argv);
            execute_input(argv, file);
        }
        else if (flag == 3)
        {
            char *argp[64];
            char *output, *file;
            if (strstr(left, "|") > 0)
            {
                char *p = strtok_r(left, "|", &file);
                parse(word, argv);
                parse(left, args);
                parse(file, argp);
                execute_pipe2(argv, args, argp);
            }
            else
            {
                parse(word, argv);
                execute_pipe(argv, left);
            }
        }
        else
        {
            parse(word, argv);
            execute(argv);
        }
    }

}

  

 

六、程序运行结果及分析

如图,为了便于观察,我用红色框框对命令和结果做了分隔。

可以看出,基本的管道和重定向功能已经实现。

 

七、实验体会

shell的实现是四个实验中需要写代码最多的一个,因为之前对这个部分的编程知识了解甚少,所以我找了很多资料学习,尤其是其中很多函数的用法,比如pipe、execvp等等有所了解。做完实验最大的感触就是很多书讲得都太笼统,只讲了一些概念和原理,但自己真正要动手实现起来,是有一定难度的。

posted @ 2014-09-15 02:11  去冰三分糖  阅读(9359)  评论(1编辑  收藏  举报