實現一個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個字符),則終止程序。輸入的命令以換行符'\n'作為結束標志。

  (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\n")== 0||strcmp(buf,"logout\n")== 0)
            break;
        for(i= 0;i< 100;i++)
        {
            arglist[i][0]= '\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!= '\n')
    {
        buf[len++]= ch;
        ch= getchar();
    }
    if(len== 256)
    {
        printf("command is too long\n");
        exit(-1);       //輸入的命令過長則退出程序
    }
    buf[len]= '\n';
    len++;
    buf[len]= '\0';
}

/*解析buf中的命令,將結果存入arglist中,命令以回車符號\n結束,如輸入命令為
 *"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]== '\n')
            break;
        if(p[0]== ' ')
            p++;
        else
        {
            q = p;
            number= 0;
            while((q[0]!= ' ')&&(q[0]!= '\n'))
            {
                number++;
                q++;
            }
            strncpy(arglist[*argcount],p,number+ 1);
            arglist[*argcount][number]= '\0';
            *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\n");
                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\n");
        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\n");
        return;
    }
    switch(how)
    {
        case 0:
            /*pid為0說明是子進程,在子進程中執行輸入的命令,輸入的命令不含>、<、和| */
            if(pid== 0)
            {
                if(!(find_command(arg[0])))
                {
                    printf("%s : command not found\n",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\n",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\n",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\n");
                    return;
                }
                else if(pid2== 0)
                {
                    if(!(find_command(arg[0])))
                    {
                        printf("%s : command not found\n",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\n");
                if(!(find_command(argnext[0])))
                {
                    printf("%s : command not found\n",argnext[0]);
                    exit(0);
                }
                fd2= open("/tmp/youdonotknowfile",O_RDONLY);
                dup2(fd2,0);
                execvp(argnext[0],argnext);
                if(remove("/tmp/youdonotknowfile"))
                    printf("remove error\n");
                exit(0);
            }
            break;
        default:
            break;
    }
    //若命令中有&,表示后台執行,父進程直接返回,不等待子進程結束
    if(background== 1)
    {
        printf("[process id %d]\n",pid);
        return;
    }
    //父進程等待子進程結束
    if(waitpid(pid,&status,0)== -1)
        printf("wait for child process error\n");
}

//查找命令中的可執行程序
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 \n");
        while((dirp= readdir(dp))!= NULL)
        {
            if(strcmp(dirp->d_name,command)== 0)
            {
                closedir(dp);
                return 1;
            }
        }
        closedir(dp);
        i++;
    }
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM