自编shell命令解释器

通过几个模型学习shell命令解释器,并尝试自己编写简易的shell

一、确定main函数

明确要做的功能,基本确定几个大函数:

void InitialEnv();                  //initial environment
void PrintSignal();             //print information
void GetInput();                    
int CommandTransform();     //return 1 for error
void Clear();                           //clear last command

其中,自己做的前面两个函数可以合并(读者可自行优化)

int main(void)
{
        printf("\n      Hello my guester!\n");
        while(TRUE){
        InitialEnv();           //initial comply's emvironment
        PrintSignal();          //print sth to recognize user to input commands
        GetInput();         
        if (strcmp(cmd[0],"exit")==0)   //exit 
        {
            printf("\n         Good bye!\n");
            break;
        }  
        if(CommandTransform())      
            printf("input error:  undefined command\n");
        Clear();
    }
    return 0;
}

大纲完成后自己就开始列写所需的全局变量了:

char *cmds;         //command
int cmds_len;       //command's length
char **cmd;         //every commands' word
int cmd_num;        //number of commands' words
int pip;            //how many pipes in command

二、功能函数的实现:
先看前两个函数,功能比较简单:
这里写图片描述
把主机名和当前目录打印一下并打印输入提示符等待输入;代码如下:

void PrintSignal()
{
    uid_t my_uid;
    struct passwd *my_info;
    my_info =getpwuid( getuid() );  
    char env[50];
    getcwd(env,50);                     
    printf( "%s=> %s$ ", my_info->pw_name,env);    
}

其中getpwuid()函数可以得到my_uid的各个属性,在这里使用获得主机名。getcwd()函数用以输出当前目录。

void InitialEnv()
{
    int i=0;
    while((cmds = (char*)malloc(sizeof(char)*100))==0);
    while((cmd = (char**)malloc(sizeof(char*)*20))==0);
    while(i<20) {
    while((cmd[i] =(char*)malloc(sizeof(char)*40))==0); 
        i = i + 1;
    }
    cmd_num = 0;
    pip = 0;
}

初始化环境函数:为存储指令的字符指针申请动态空间(malloc函数)

将输入存入字符串:

void GetInput()
{
    int i = 0;
    int j = 0;
    //freopen("/dev/tty","r",stdin);    
    gets(cmds);                         
    while (cmds[i]!= '\0')                      
    {
        if(cmds[i]!=' ')
            cmd[cmd_num][j++]=cmds[i];
        else
        {
            if ((cmds[i+1]!='\0')&&(cmds[i+1]!=' '))
                cmd_num++;
            j = 0;
        }
        i++;
    }
    cmd_num++;  
}

gets()函数得到输入的字符串 以’\n’结束

接下来实现比较复杂的解释输入的“指令”的函数,以下是包含了管道的实现的代码,可能比较繁冗,读者看思路即可:
函数思路:
当还有命令词未判断时循环:判断是否为第一个参数->判断是否包含管道->执行相应的命令(内部和外部,其中外部命令是指在unix操作系统下以文件的形式存在的命令函数,故可用fork-execve-wait-exit模型来调用,切记要让子进程exit)

int CommandTransform()
{
    int fd[10][2];
    int pip = -1;
    int i = 0;
    pid_t pid;
    if (strlen(cmd[0])!=0)
    while(i<cmd_num)
    {
        if (strcmp(cmd[i],"cd") == 0)
        {   
            if (i==0)
            {
                int fp;
                if (strlen(cmd[1])==0)  return 0;       //cd command can't use the pipe '|'
                if(strlen(cmd[2])!=0)   return 1;

                const char* p = cmd[1];

                if((fp=open(p,O_RDONLY)) < 0)           //find the path
                        {
                                printf("Can not open the directory %s\n",cmd[1]);
                                return 0;
                        }
                if (fchdir(fp) < 0)                 //call fchdir() to change dir
                {
                    printf("cd failed,try again\n");
                    return 0;
                }
                return 0;
            }
            else 
            {
                close(0);
                dup2(fd[pip][0],0);;
                close(fd[pip][1]);
                read(fd[pip][0],cmd[i+1],40);
                dup2(0,fd[pip][0]);

                const char* p = cmd[i+1];
                int fp;


                if((fp=open(p,O_RDONLY)) < 0)           //find the path
                        {
                                printf("Can not open the directory %s\n",p);
                                return 0;
                        }
                if (fchdir(fp) < 0)                 //call fchdir() to change dir
                {
                    printf("cd failed,try again\n");
                    return 0;
                }
                return 0;
            }
        }

        else if (strcmp(cmd[i],"echo") == 0)
        {
            if (i==0)
            {
                if (strlen(cmd[2])==0)
                {
                    printf("%s\n",cmd[i+1]);
                    return 0;       
                }
                else if (strcmp(cmd[2],"|")==0)
                {
                    if(pipe(fd[++pip])<0)  
                    {  
                        printf("pipe error!/n");  
                            return 0;  
                    }  
                    if((pid = fork())<0)  
                    {  
                            printf("fork error!/n");  
                            return 0;  
                    }  
                    else if(pid == 0)
                    {
                        close(1);
                        dup2(fd[pip][1],1);
                        close(fd[pip][0]);
                        write(fd[pip][1],cmd[1],40);
                        exit(0);
                    }
                    else if (pid >0)
                    {
                        waitpid(pid,NULL,0);
                        i+=3;
                    }
                }
                else return 1;
            }
            else 
            {
                if(strlen(cmd[i+1])==0)
                {
                    close(0);
                    dup2(fd[pip][0],0);
                    close(fd[pip][1]);
                    read(fd[pip][0],cmd[i+1],40);
                    dup2(0,fd[pip][0]);
                    printf("%s\n",cmd[i+1]);
                    return 0;
                }
                else if (cmd[i+1]!="|")
                {
                    printf("input --error!\n");
                    return 0;
                }
                else
                {
                    close(0);
                    fd[pip][0]=dup(0);
                    close(fd[pip][1]);
                    read(fd[pip][0],cmd[i+1],40);
                    if(pipe(fd[++pip])<0)  
                    {  
                        printf("pipe error!/n");  
                            return 0;  
                    }
                    close(1);
                    fd[pip][1] = dup(1);
                    close(fd[pip][0]);
                    write(fd[pip][1],cmd[i+1],40);  
                    i += 2;
                }
            }

        }
        else if (strcmp(cmd[i],"wc") == 0)
        {
            if (i==0)
            {
                FILE *fp;
                int c;
                    int res_current[3]={0};             
                const char* p = cmd[1];

                if((fp=fopen(p,"r"))==(FILE*)NULL)
                    {
                             c=3;                   //"wc " takes 3 room
                         while(cmds[c]!='\0')
                         {
                        int flag;
                        res_current[2]++;
                        if(c=='\n')
                        {
                            res_current[0]++;
                        }
                                    c++;
                                }           
                                printf("line: %d  word: %d  character: %d\n",res_current[0],cmd_num-1,res_current[2]);
                    return 0;
                            }
                            c=fgetc(fp);                        //file-get-char function
                    while(c!=EOF)
                    {
                    int flag;
                    res_current[2]++;
                    if(c=='\n')
                    {
                        res_current[0]++;
                    }
                    if(c=='\t' || c== ' ' || c=='\n')
                    {
                                if(flag==0)
                                {
                                    res_current[1]++;
                                        flag=1;
                            }
                    }
                        else flag=0;  
                                c=fgetc(fp);
                            }
                            fclose(fp);             
                            printf("line: %d  word: %d  character: %d        in '%s'\n",res_current[0],res_current[1],res_current[2],p);
                return 0;
            }
            else 
            {
                if((pid = fork())<0)  
                {  
                    printf("fork error!/n");  
                        return 0;  
                }  
                if(pid == 0)
                {
                    FILE * fp;
                    close(0);
                    dup2(fd[pip][0],0);
                    close(fd[pip][1]);
                    read(fd[pip][0],cmd[i+1],40);
                    dup2(0,fd[pip][0]);
                        int res_current[3]={0};     

                    if (strlen(cmd[i+1])==0)
                    {
                        printf("open error!\n");
                        return 0;
                    }

                    if((fp=fopen(cmd[i+1],"r"))==(FILE*)NULL)
                    {
                                    int c=0;                    
                                while(cmd[i+1][c]!='\0')
                                {
                            int flag;
                            res_current[2]++;
                            if(c=='\n')
                            {
                                res_current[0]++;
                            }
                                if(c=='\t' || c== ' ' || c=='\n')
                                {
                                        res_current[1]++;
                            } 
                                            c++;
                                    }           
                                    printf("line: %d  word: %d  character: %d\n",res_current[0]+1,res_current[1]+1,res_current[2]);
                        exit(0);
                    }
                    else 
                    {
                        int c=fgetc(fp);                    //file-get-char function
                                while(c!=EOF)
                                {
                            int flag;
                            res_current[2]++;
                            if(c=='\n')
                            {
                                res_current[0]++;
                            }
                                if(c=='\t' || c== ' ' || c=='\n')
                                {
                                        res_current[1]++;
                            }
                                            c=fgetc(fp);
                                    }
                                    fclose(fp);             
                                    printf("line: %d  word: %d  character: %d        in '%s'\n",res_current[0],res_current[1],res_current[2],cmd[i+1]);
                        exit(0);
                    }
                }
                else if (pid > 0) waitpid(pid,NULL,0);
                return 0;
            }
        }
        else if (strcmp(cmd[i],"ls") == 0)
        {
            if (i==0)
            {
                if (strlen(cmd[1])==0)
                {
                    char *argv[]={"ls","-al",(char*)0}; 
                    char *envp[] = {"PATH=/bin",0};
                    if((pid = fork())==0)
                    {
                        execve("/bin/ls",argv,envp);
                        exit(0);
                    }
                    else if (pid > 0) waitpid(pid,NULL,0);
                    return 0;       
                }
                else if(cmd[1]=="|")
                {   
                    if(pipe(fd[++pip])<0)  
                    {  
                        printf("pipe error!/n");  
                            return 0;  
                    }  
                    char *argv[]={"ls","-al",(char*)0}; 
                    char *envp[] = {"PATH=/bin",0};
                    if((pid = fork())==0)
                    {
                        close(1);
                        dup2(fd[pip][1],1);
                        close(fd[pip][0]);
                        execve("/bin/ls",argv,envp);
                        exit(0);
                    }
                    else if (pid > 0) waitpid(pid,NULL,0);
                    i+=2;   
                }
                else
                {
                    if (cmd[2]=="|")
                    {
                        if(pipe(fd[++pip])<0)  
                        {  
                            printf("pipe error!/n");  
                                return 0;  
                        }  
                        char *argv[]={"ls","-al",(char*)0}; 
                        char *envp[] = {"PATH=/bin",0};
                        if((pid = fork())==0)
                        {
                            close(1);
                            dup2(fd[pip][1],1);
                            close(fd[pip][0]);
                            if  (chdir(cmd[1])==-1) 
                            {
                                printf("no such directory.\n");
                                exit(0);                
                            }
                            execve("/bin/ls",argv,envp);
                            exit(0);
                        }
                        else if (pid > 0) waitpid(pid,NULL,0);
                        i+=2;   
                    }
                    else if(strlen(cmd[2])==0)
                    {
                        char *argv[]={"ls","-al",(char*)0}; 
                        char *envp[] = {"PATH=/bin",0};
                        if((pid = fork())==0)
                        {
                            if  (chdir(cmd[1])==-1) 
                            {
                                printf("no such directory.\n");
                                exit(0);                
                            }
                            execve("/bin/ls",argv,envp);
                            exit(0);
                        }
                        else if (pid > 0) waitpid(pid,NULL,0);
                        return 0;
                    }
                    else return 1;
                } 
            }
            else                        //   ls  i!=0
            {
                if(strlen(cmd[i+1])==0)
                {
                    char *argv[]={"ls","-al",(char*)0}; 
                    char *envp[] = {"PATH=/bin",0};

                    close(0);
                    dup2(fd[pip][0],0);
                    close(fd[pip][1]);
                    read(fd[pip][0],cmd[i+1],40);
                    dup2(0,fd[pip][0]);

                    if (strlen(cmd[i+1])==0) 
                    {
                        printf("error: ls needs a dir input.\n");
                        return 1;
                    }

                    if((pid = fork())==0)
                    {
                        if  (chdir(cmd[i+1])==-1)   
                        {
                            printf("no such directory.\n");
                            exit(0);                
                        }
                        execve("/bin/ls",argv,envp);
                        exit(0);
                    }
                    else if (pid > 0) waitpid(pid,NULL,0);
                    return 0;
                }
                else if (cmd[i+1]=="|")
                {
                    char *argv[]={"ls","-al",(char*)0}; 
                    char *envp[] = {"PATH=/bin",0};

                    close(0);
                    dup2(fd[pip][0],0);
                    close(fd[pip][1]);
                    read(fd[pip][0],cmd[i+1],40);

                    if(pipe(fd[++pip])<0)  
                    {  
                        printf("pipe error!/n");  
                            return 0;  
                    }

                    if((pid = fork())==0)
                    {
                        close(1);
                        dup2(fd[pip][1],1);
                        close(fd[pip][0]);
                        if  (strlen(cmd[i+1])!=0&&chdir(cmd[i+1])==-1)  
                        {
                            printf("no such directory.\n");
                            exit(0);                
                        }
                        execve("/bin/ls",argv,envp);
                        exit(0);
                    }
                    else if (pid>0) waitpid(pid,NULL,0);
                    i+=2;
                }
                else return 1;
            }
        }else return 1;
    }
}

最后一个清零函数主要来将字符串清零,以避免对下次输入造成影响。

void Clear()
{
    int i=0;
    memset(cmds, 0, sizeof(char *));                //clear cmds
    while (i < cmd_num) 
        memset(cmd[i++], 0, sizeof(char *));            //clear cmd[i]
    memset(cmd, 0, sizeof(char **));                //clear cmd 
}

其中涉及到的头文件如下:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

运行截图:
欢迎界面
echo
ls
wc
cd
管道
exit
至此,一个简单的shell命令解释器就完成了,有几个涉及到管道的蛋疼的功能还是有点烂尾,读者可在此基础上修改。

猜你喜欢

转载自blog.csdn.net/wm64195135/article/details/44920233