教你寫一個簡單的myshell

一.myshell的功能
1. ls
2. 輸入輸出重定向
3. 管道操作
4. linux的內置命令 cd
5. 簡單的history
6. 屏蔽ctrl + c
二.主要難點
1.命令解析
2.進程的應用
3.cd命令的輸出格式
三.代碼

#include<stdio.h>
# include <sys/types.h>
# include <unistd.h>
# include <sys/wait.h>
# include <stdlib.h>
# include <string.h>
# include <dirent.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <linux/limits.h>
# include <errno.h>
# include <signal.h>
# define P 3 // 管道
# define I 2 // 輸入重定向
# define V 4 // 兩個>>輸出流重定向
# define O 1 // 輸出重定向
# define N 0 // 一般的命令
void output( char argv[][256], int sum );
void analyse(int , char fen[][256]);//執行參數
void fenge( char *buf, char fen[][256], int *len );
int find_mingli(char *canshu[256]);//一次只傳進來一個,所以用一維的
void my_error( const char * err_string, int line )
{
    fprintf(stderr,"line : %d\n", line);
    perror( err_string );
    exit(-1);
}
void my_cd(char fen[][256], char str[256], int len)
{
    char buf[256]="/home/zongjin/";
    char *arg2[256];
    int i;
    for( i = 0; i<len; i++ )
    {
        arg2[i] = (char*)fen[i];
        arg2[i][strlen(arg2)] = '\0';
    }
    arg2[i] = NULL;
    if(arg2[1] == NULL)
    {
        chdir(buf);
        strcpy(str,buf);
        str[strlen(str)]='\0';
    }
    if( arg2[1] != NULL )
    {
        chdir(arg2[1]);

        strcpy(str,arg2[1] );
        str[strlen(str)]='\0';
    }
}
void xinhao(int signo, sigset_t * newmask, sigset_t * oldmask)
{
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);
}
sigset_t newmask, oldmask;
int main(void)
{
    int i=0, len,j;
    char buf[256];
    char argv[100][256];
    char fen[100][256];
    char str[256]= "mashell:";
    int fd;

//屏蔽ctrl - c
    sigemptyset(&newmask);//建立一個信號箱
    sigaddset(&newmask, SIGINT);//將ctrl+c的信號放到信號箱內

    while(1)
    {
        len =0;
        printf("%s:",str);
        signal(SIGINT, xinhao);//調用函數xinhao
        gets(argv[i]);
        strcpy(buf, argv[i]);
        buf[strlen(buf)] = '\0';

        if( (strcmp(buf, "exit") ==0 )  || (strcmp(buf, "logout" ) == 0))
        {
            break;
        }
        i++;
        if( strcmp( buf, "history" ) == 0 )
        {
            output(argv, i);
        }
        fenge(buf, fen, &len);

        if( strcmp( fen[0], "cd" ) == 0 )
        {
            strcpy( str, "mashell:");
            char str2[256];
           my_cd(fen, str2,len);
            strcat( str,str2 );
           str[strlen(str)]='\0';
            str2[256]=NULL;
            continue;
        }
        analyse(len, fen);


    }
    exit(0);

}
void output( char argv[100][256], int sum )
{
    int i;
    char buf[256];
    for( i = 0; i<sum; i++ )
    {
        printf("%d:%s\n",i+1,argv[i]);
    }
}
//命令解析,以空格進行分割
void fenge( char * buf, char fen[100][256], int * len )
{
    int i, j=0;
    char p[256];//這塊不能寫成char *p的形式,否則出現段錯誤
    for( i = 0; buf[i]!= NULL; i++ )
    {
        p[j] = buf[i];
        j++;

        if( buf[i] == ' ')
        {
            strcpy(fen[*len], p );
            fen[*len][j-1]= '\0';
            j=0;
            *len = *len +1;
        }
        if( buf[i+1] == '\0' )
        {
            strcpy(fen[*len], p);
            fen[*len][j]= '\0';
            *len= *len +1;// 這塊不能寫成*len++這樣len的值不變
            j=0;
        }
    }

}
//分析命令
void analyse(int len, char fen[100][256])
{
    int i,fd, status, pid2,fd2,ccc;
    int t=0;//收納各種符號
    int sum;//以空格記錄命令的個數
    int hou;//單個記錄後臺符的個數,是一個輔助命令,所以單個記錄下來
    int flag=0;//記錄各種輸入的各種符號的個數
    pid_t pid;
    pid_t child_pid;
    char *file;
    char *arg[256];
    char *str = "cd";

    char* argnext[len+1];
    sum = len;
    hou = 0;
    for( i = 0; i<sum; i++ )
    {
        arg[i] = (char*)fen[i];
        arg[i][strlen(arg[i])] = '\0';

    }
    arg[i] = NULL;

    for( i = 0; i< sum; i++ )
    {
        if( strcmp(arg[i], "&") ==0 )
        {
            if( i ==sum-1 )
            {
                hou = 1; 
                arg[i] == NULL;

            }
            else
            {
                printf("輸入錯誤,後臺符必須放到最後!\n");
                exit(-1);
            }
        }
    }

    //判斷有沒有輸出重定向符號'>'並且必須讓符號的後面必須要有文件名
    for( i = 0; arg[i]!=NULL; i++ )
    {
        if( strcmp( arg[i], ">" ) == 0  && arg[i+1] != NULL)
        {
            flag +=1;

            if( flag ==1 )
            {
                t =O;
                file = arg[i+1];
                arg[i] =NULL;
            }
        puts(file);

            if( flag>1 )
            {
               printf("輸入錯誤\n");
                return ;
            }      
        }


    //判斷有沒有輸入重定向'<'並且必須讓這個符號前面有東西


        if(arg[i] != NULL && strcmp( arg[i], "<" ) == 0 && i!=0)
        {
            flag +=1;
            if( flag ==1 )
            {
                t =I;
                file = arg[i+1];
                arg[i] =NULL;
            }
            else
            {
                printf("輸入錯誤\n");
                return ;
            }
        }   

        //命令只有一個管道'|' 並且它的前後必須都要有東西,還要將它後面的命令記錄下來
        if ( arg[i] != NULL && strcmp( arg[i], "|" ) == 0 && i!=0 && arg[i+1] != NULL )
        {
            arg[i] = NULL;
            flag +=1;
            if(flag ==1)
            {
              t = P;
              int j;
               for( j = i+1; arg[j] != NULL; j++ )
              {
                argnext[j-i-1] = arg[j];
              }
              argnext[j-i-1] = arg[j];
              break;

            }
            else
            {
                printf("輸入錯誤!\n");
                return;
            }

        }

    }

    if( ( pid = fork() ) < 0 )
    {
        printf("fork error\n");
        return;
    }


    switch(t)
    {
        case 0:
        if(pid == 0)
        {
            if(!( find_mingli(arg)))
            {
            printf("系統目錄下沒有%s這個命令\n",arg[0]);
            exit(0);
            }
       if(   execvp(arg[0],arg) ==-1)
            {
                perror("execvp");
            }
          exit(0);

        }
        break;
        case 1://輸出流重定向
        if( pid == 0 )
        {
            if( !( find_mingli(arg) ) )
            {
                printf("沒有這個%s命令\n", arg[0]);
                exit(0);
            }
         fd = open(file, O_RDWR|O_CREAT|O_TRUNC, 0644);
            if( fd < 0 )
            {
                my_error("open", __LINE__);
            }

            dup2( fd, 1 );
            execvp(arg[0], arg);
            exit(0);

        }
        break;
        case 2://輸入流重定向
        if( pid == 0 )
        {
            if( !find_mingli( arg ) )
            {
                printf("沒有這個命令%s\n", arg[0]);
                exit(0);
            }
            if( (fd = open( file, O_RDONLY )) < 0 )
            {
                my_error("open", __LINE__);
            }
            dup2( fd, 0 );
            execvp(arg[0], arg);
            exit(0);
        }
    break;
       case 3://在子進程中先執行管道符前面的,在父進程中執行後面的,父進程等待子進程結束
        if( pid == 0 )
        {

            if( (pid2 = fork()) <0 )
            {
                printf("make process erron\n");
                exit(-1);
            }
            if( pid2==0 )//在子進程中先執行管道符前面的
            {
              if( !find_mingli(arg) )
                {
                    printf("沒有這個命令%s\n",arg[0]);
                    exit(-1);
                }
                if( (fd2 = open("/tmp/xiaoming", O_RDWR|O_CREAT|O_TRUNC, 0644)) <0 )
                {
                    my_error("open", __LINE__);
                    exit(-1);
                }
                dup2( fd2, 1 );//將父進程文件的文件描述符複製給新的FD2,這樣fd2中就包含了父進程的所有信息
                execvp( arg[0], arg );//在環境變量中查找可執行的程序,執行參數arg中的命令
                exit(0);//關閉進程,他有一個返回值,在8到15之間,這個值放到內核中等待父進程通過wait(&reslt)來取這個值

            }
            if( waitpid(pid2, &ccc, 0  ) <0 )//父進程等待子進程結束
            {
                printf("等待失敗!\n");
            }
            if( !find_mingli(argnext) )
            {
                printf("沒有這個命令%s\n", argnext[0]);
                exit(-1);
            }
            fd2 = open("/tmp/xiaoming", O_RDONLY);//打開子進程的那個文件,輸入到當前的程序中,執行新的參數
            dup2(fd2, 0);
            execvp(argnext[0], argnext);
            if( remove( "/tmp/xiaoming" ) )//正確移除返回0, 沒有移除返回-1
            {
                printf("remove error\n");
            }
            exit(0);

        }
        break;
    default :
        break;



     }
     if( hou == 1 )
     {
         return ;
     }

     if( waitpid( pid, &status, 0) == -1 )
     {
         printf(" 等待錯誤  !\n");
     }





}//這個函數寫完,有參數 t , arg , file ,  
int find_mingli(char *arg[256])//這個函數的作用是判斷你輸入的命令在系統的命令目錄裏只否有,有則返回1,無則返回0
{
    DIR *dir;
    struct dirent *ptr;
    int i=0;

    char *path[] = {"./", "/bin", "/usr/bin", NULL};

    //是當前目錄下的程序可以正常運行
      if( strncmp( arg[0], "./", 2 ) == 0 )
    {
        arg[0] = arg[0] +2;//暫時不知道在什麼情況下用
    }

    while(path[i]!= NULL)
    {
       if( ( dir = opendir(path[i])  ) <0 )
        {
            printf("沒有這個參數 /bin\n");
        }
        while( (ptr = readdir(dir) ) != NULL )
        {
            if( strcmp( ptr->d_name , arg[0]) == 0 )
            {
                closedir( dir);
                return 1;
            }
        }
        closedir(dir);
        i++;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章