• 实现一个shell程序


       实现一个自己的shell程序,这个程序有这些功能:解释执行命令,支持输入输出重定向,支持管道,后台运行

    程序。当运行该程序后,它支持以下的命令格式:

       1.单个命令,如:ls。2.带l到多个参数的命令,如ls -l。3.带一个输出重定向的命令。4.带一个输入重定向的

    命令。5.带一个管道的命令。6.后台运行符&可加在各个命令的最后面。7.输入exit或logout退出myshell。

       错误处理:1.输入错误的命令格式报错。2.输入不存在的命令报错。

       程序主函数的流程图如下:

                               

       程序的各个函数的功能及说明:

      (1)void print_prompt():该函数只是简单地打印myshell的提示符,即“myshell$”。

      (2)void get_input(char *buf):获得一条用户输入的待执行命令,参数buf用于存放输入的命令。如果输入的

    命令过长(大于256个字符),则终止程序。输入的命令以换行符' '作为结束标志。

      (3)void explain_input(char *buf,int *argcount,char arglist[100][256]):解析buf中存放的命令,把每个

    选项存放在arglist中。

      (4)do_cmd(int argcount,char arglist[100][256]):执行arglist中存放的命令,arglist为待执行命令的参数

    的个数。

      (5)int find_command(char *command):功能是分别在当前目录下、/bin、/usr/bin目录下查找命令的可执行程

    序。

       下面是该程序的源代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #define normal 0        //一般的命令
    #define out_redirect 1  //输出重定向
    #define in_redirect 2   //输入重定向
    #define have_pipe 3     //命令中有管道
    
    void print_prompt();         //打印提示符
    void get_input(char*);       //得到输入的命令
    void explain_input(char*,int*,char a[][256]);   //对输入命令进行解析
    void do_cmd(int,char a[][256]); //执行命令
    int find_command(char*);     //查找命令中的可执行程序
    
    int main(int argc,char *argv[])
    {
        int i;
        int argcount= 0;
        char arglist[100][256];
        char *buf= NULL;
        char **arg= NULL;
        buf= (char*)malloc(256);
        if(buf == NULL)
        {
            perror("malloc failed");
            exit(-1);
        }
        while(1)
        {
            memset(buf,0,256);     //将buf所指向的空间清零
            print_prompt();
            get_input(buf);
            //若输入的命令为exit或logout则退出本程序
            if(strcmp(buf,"exit
    ")== 0||strcmp(buf,"logout
    ")== 0)
                break;
            for(i= 0;i< 100;i++)
            {
                arglist[i][0]= '';
            }
            argcount= 0;
            explain_input(buf,&argcount,arglist);
            do_cmd(argcount,arglist);
        }
        if(buf!= NULL)
        {
            free(buf);
            buf= NULL;
        }
        return 0;
    }
    
    void print_prompt()
    {
        printf("myshell$ ");
    }
    
    //获取用户输入
    void get_input(char *buf)
    {
        int len= 0;
        int ch;
        ch= getchar();
        while(len< 256&&ch!= '
    ')
        {
            buf[len++]= ch;
            ch= getchar();
        }
        if(len== 256)
        {
            printf("command is too long
    ");
            exit(-1);       //输入的命令过长则退出程序
        }
        buf[len]= '
    ';
        len++;
        buf[len]= '';
    }
    
    /*解析buf中的命令,将结果存入arglist中,命令以回车符号
    结束,如输入命令为
     *"ls -l /tmp",则arglist[0]、arglist[1]、arglist[2]分别为ls、-l、/tmp */
    void explain_input(char *buf,int *argcount,char arglist[100][256])
    {
        char *p= buf;
        char *q= buf;
        int number= 0;
        while(1)
        {
            if(p[0]== '
    ')
                break;
            if(p[0]== ' ')
                p++;
            else
            {
                q = p;
                number= 0;
                while((q[0]!= ' ')&&(q[0]!= '
    '))
                {
                    number++;
                    q++;
                }
                strncpy(arglist[*argcount],p,number+ 1);
                arglist[*argcount][number]= '';
                *argcount= *argcount+ 1;
                p = q;
            }
        }
    }
    
    void do_cmd(int argcount,char arglist[100][256])
    {
        int flag= 0;
        int how= 0;            //用于指示命令中是否含有>、<、|
        int background= 0;     //标识命令中是否有后台运行标识符&
        int status;
        int i;
        int fd;
        char* arg[argcount+ 1];
        char* argnext[argcount+ 1];
        char* file;
        pid_t pid;
        //将命令取出
        for(i= 0;i< argcount;i++)
        {
            arg[i]= (char*)arglist[i];
        }
        arg[argcount]= NULL;
        //查看命令行是否有后台运行符
        for(i= 0;i< argcount;i++)
        {
            if(strncmp(arg[i],"&",1)== 0)
            {
                if(i== argcount-1)
                {
                    background= 1;
                    arg[argcount-1]= NULL;
                    break;
                }
                else
                {
                    printf("wrong command
    ");
                    return;
                }
            }
        }
        for(i= 0;arg[i]!= NULL;i++)
        {
            if(strcmp(arg[i],">")== 0)
            {
                flag++;
                how= out_redirect;
                if(arg[i+ 1]== NULL)
                    flag++;
            }
            if(strcmp(arg[i],"<")== 0)
            {
                flag++;
                how= in_redirect;
                if(i== 0)
                    flag++;
            }
            if(strcmp(arg[i],"|")== 0)
            {
                flag++;
                how= have_pipe;
                if(arg[i+ 1]== NULL)
                    flag++;
                if(i== 0)
                    flag++;
            }
        }
        /* flag大于1,说明命令中含有多个>,<,|符号,本程序是不支持这样的命令的,或者命令格式不对,如"ls -l /tmp >" */
        if(flag> 1)
        {
            printf("wrong command
    ");
            return;
        }
        if(how== out_redirect)
        {
            //命令只含有一个输出重定向符号
            for(i= 0;arg[i]!= NULL;i++)
            {
                if(strcmp(arg[i],">")== 0)
                {
                    file= arg[i+ 1];
                    arg[i]= NULL;
                }
            }
        }
        if(how== in_redirect)
        {
            //命令中只含有一个输入重定向符号
            for(i= 0;arg[i]!= NULL;i++)
            {
                if(strcmp(arg[i],"<")== 0)
                {
                    file= arg[i+ 1];
                    arg[i]= NULL;
                }
            }
        }
        if(how== have_pipe)
        {
            /*命令只含有一个管道符号|,把管道符号后面的部分存入argnext中,管道后面的部分是一个可执行的shell命令 */
            for(i= 0;arg[i]!= NULL;i++)
            {
                if(strcmp(arg[i],"|")== 0)
                {
                    arg[i]= NULL;
                    int j;
                    for(j= i+ 1;arg[j]!= NULL;j++)
                    {
                        argnext[j-i-1]= arg[j];
                    }
                    argnext[j-i-1]= arg[j];
                    break;
                }
            }
        }
        if((pid= fork())< 0)
        {
            printf("fork error
    ");
            return;
        }
        switch(how)
        {
            case 0:
                /*pid为0说明是子进程,在子进程中执行输入的命令,输入的命令不含>、<、和| */
                if(pid== 0)
                {
                    if(!(find_command(arg[0])))
                    {
                        printf("%s : command not found
    ",arg[0]);
                        exit(0);
                    }
                    execvp(arg[0],arg);
                    exit(0);
                }
                break;
            case 1:
                //输入的命令中含有输出重定向符>
                if(pid== 0)
                {
                    if(!(find_command(arg[0])))
                    {
                        printf("%s : command not found
    ",arg[0]);
                        exit(0);
                    }
                    fd= open(file,O_RDWR|O_CREAT|O_TRUNC,0644);
                    dup2(fd,1);
                    execvp(arg[0],arg);
                    exit(0);
                }
                break;
            case 2:
                //输入的命令中含有输入重定向符<
                if(pid== 0)
                {
                    if(!(find_command(arg[0])))
                    {
                        printf("%s : command not found
    ",arg[0]);
                        exit(0);
                    }
                    fd= open(file,O_RDONLY);
                    dup2(fd,0);
                    execvp(arg[0],arg);
                    exit(0);
                }
                break;
            case 3:
                //输入的命令中含有管道符|
                if(pid== 0)
                {
                    int pid2;
                    int status2;
                    int fd2;
                    if((pid2= fork())< 0)
                    {
                        printf("fork2 error
    ");
                        return;
                    }
                    else if(pid2== 0)
                    {
                        if(!(find_command(arg[0])))
                        {
                            printf("%s : command not found
    ",arg[0]);
                            exit(0);
                        }
                        fd2= open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,0644);
                        dup2(fd2,1);
                        execvp(arg[0],arg);
                        exit(0);
                    }
                    if(waitpid(pid2,&status2,0)== -1)
                        printf("wait for child process error
    ");
                    if(!(find_command(argnext[0])))
                    {
                        printf("%s : command not found
    ",argnext[0]);
                        exit(0);
                    }
                    fd2= open("/tmp/youdonotknowfile",O_RDONLY);
                    dup2(fd2,0);
                    execvp(argnext[0],argnext);
                    if(remove("/tmp/youdonotknowfile"))
                        printf("remove error
    ");
                    exit(0);
                }
                break;
            default:
                break;
        }
        //若命令中有&,表示后台执行,父进程直接返回,不等待子进程结束
        if(background== 1)
        {
            printf("[process id %d]
    ",pid);
            return;
        }
        //父进程等待子进程结束
        if(waitpid(pid,&status,0)== -1)
            printf("wait for child process error
    ");
    }
    
    //查找命令中的可执行程序
    int find_command(char *command)
    {
        DIR *dp;
        struct dirent *dirp;
        char *path[]= {"./","/bin","/usr/bin",NULL};
        //使当前目录下的程序可以运行,如命令"./fork"可以被正确解释和执行
        if(strncmp(command,"./",2)== 0)
            command= command+ 2;
        //分别在当前目录、/bin和/usr/bin目录查找要执行的程序
        int i= 0;
        while(path[i]!= NULL)
        {
            if((dp= opendir(path[i]))== NULL)
                printf("can not open /bin 
    ");
            while((dirp= readdir(dp))!= NULL)
            {
                if(strcmp(dirp->d_name,command)== 0)
                {
                    closedir(dp);
                    return 1;
                }
            }
            closedir(dp);
            i++;
        }
        return 0;
    }
  • 相关阅读:
    beautifulsoup的一些使用
    requests(爬虫常用)库的使用
    find a maximum product triplet in this array
    Minimum difference between two arrays
    [LeetCode] Binary Tree Upside Down
    n 刀切多少块pizza
    Biased Random Number Generator
    linked list焦点问题,面经里很多,考虑相交不相交,有环无环 + Find Leaves of Binary Tree (Java)
    replace string use another
    Union and Intersection of two sorted list
  • 原文地址:https://www.cnblogs.com/XNQC1314/p/9196815.html
Copyright © 2020-2023  润新知