教你写一个简单的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;
}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值