shell的自己实现

shell主要实现了管道,重定向输入输出,后台运行,历史命令查询,cd,自动补全

  1. 简单历史命令查询主要用到的readline库中history中add_history函数,同时实现了上下键查找历史命令
    同样TAB的自动补全命令也是通过readline()函数来实现

  2. 输入重定向:把<右边的文件的内容输入到<左边的命令中。
    输出重定向:把运行>左边命令得出的结果输入到>右边的文件中。
    (若以>输出到一个已经存在的文件中,那么文件就会被覆盖掉,如果想追加内容而不想删除原来文件的内容则可以用’>>’)
    管道命令:a|b,a和b分别是两个命令,执行a命令后得到的结果可以被b接受,b命令继续对结果进行相应的处理。

(输入重定向)

 if(pid == 0)
            {
                if(!(find_command(arg[0])))
                {
                    printf("%s:command not found",arg[i]);
                    exit(0);
                }
               fd=open(file,O_RDONLY);
                dup2(fd,0);                                        
               execvp(arg[0],arg);
                exit(0);
            }

(输出重定向)

if(pid == 0)
            {
                if(!(find_command(arg[0])))
                {
                    printf("%s:command not found",arg[i]);
                    exit(0);
                }
               、 fd=open(file,O_RDONLY);
                    dup2(fd,0);                                        
                    execvp(arg[0],arg);
                    exit(0);
            }

输入重定向和输出重定向

输入重定向和输出重定向的方法很相似,主要都是运用了dup2()这个函数:

dup2( int oldfd , int newfd )

dup2可以用参数newfd指定新文件描述符的数值,dup2调用成功,返回新的描述符。

(文件描述符0代表标准输入文件,一般就是键盘; 文件描述符1代表标准输出文件,一般是显示器; 文件描述符2代表标准错误输出,一般也指显示器 )

dup2(fd,1)的意思就是把文件描述符替换为1,因为执行命令得到的结果是会向名为1的输出流中输出得到的结果,现在将文件描述符的数值变为1,执行命令后自然就会将结果输入到文件中了。

dup2(fd,0)同理,将文件符的值变为0,命令会从名为0的输入流中读取信息。

  1. 其实管道命令的实现方法也很简单,也运用了输入输出重定向

实现的方法就是将|左边的命令执行的结果保存至文件中,在运行|右边的命令时将再将刚刚文件中的内容传入|右边的命令去执行。

(管道命令)

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("please wait process error\n");


                if(!(find_command(arg[i])))
               {
                    printf("%s :command not found\n",arg[0]);
                    exit(0);
              }
                fd2 = open("/tmp/youdonotknowfile",O_WRONLY|O_CREAT|O_TRUNC,0644);                     

               dup2(fd2,0);
               execvp(argnext[0],argnext);
                if(remove("/tmp/youdonotknowfile"))
                printf("remove error\n");
                exit(0);
        }

code of myshell

main.c

/*************************************************************************
    > File Name: main.c
    > Author: YinJianxiang
    > Mail: YinJianxiang123@gmail.com
    > Created Time: 2017年07月26日 星期三 11时48分54秒
 ************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
#include "./myhead.h"
#include "./func.h"



int main(int argc, char *argv[]) {
    int i; 
    int argcount = 0;                   
    char arglist[100][256];
    char **arg = NULL;
    char *buf = NULL;
    char  histroy[100][256];
    int his_count;
    char temp[256];

    buf = (char *)malloc(256);
    if(buf == NULL) {
        perror("malloc faid");
        exit(-1);
    }

    while(1) {
        signal(SIGINT,SIG_IGN);
        signal(SIGQUIT,SIG_IGN);
        signal(SIGSTOP,SIG_IGN);
        signal(SIGTSTP,SIG_IGN);
        memset(buf,0,256);
        //print_prompt();                 //显示当前路径

        get_input(buf,&his_count,histroy);                 //接受命令

        /*exit或者logout退出*/
        if(strcmp(buf,"exit") == 0 || strcmp(buf,"logout") == 0) {
            break;                  
        }
        for(i = 0;i < 100;i++) {
            arglist[i][0] = 0;          //初始化命令
        }
        argcount = 0;

        explain_input(buf,&argcount,arglist);  //解析命令
        do_cmd(argcount,arglist,his_count,histroy);              //执行命令
    }


    getcwd(temp,100);
    strcmp(last_dir,temp);
    if(buf != NULL) {                          //释放空间
        free(buf);
        buf = NULL;
    }

    return 0;
}

myhead.h

/*************************************************************************
    > File Name: myhead.h
    > Author: YinJianxiang
    > Mail: YinJianxiang123@gmail.com
    > Created Time: 2017年07月26日 星期三 11时10分12秒
 ************************************************************************/

#ifndef _MYHEAD_H
#define _MYHEAD_H

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

#define normal               0
#define out_redirect         1 
#define double_out_redirect  2
#define in_redirect          3
#define have_pipe            4

#define is_background   1
#define not_background  0                  

void print_prompt();                    //打印开始时的所在路径            
void get_input(char *buf,int *his_count,char history[100][256]);                       //获得用户输出        
void explain_input(char *buf,int *argcount,char arglist[100][256]);     //接受输入
void do_cmd(int argcount,char arglist[100][256],int his_count,char history[100][256]);     //执行命令

char last_dir[256];

#endif

func.h

/*************************************************************************
    > File Name: func.h
    > Author: YinJianxiang
    > Mail: YinJianxiang123@gmail.com
    > Created Time: 2017年07月26日 星期三 11时48分40秒
 ************************************************************************/


#ifndef _FUNC_H
#define _FUNC_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <dirent.h>
#include "./myhead.h"

static int find_command(char *command);            //查找命令是否存在

//得到输入,统计历史命令个数并存储
void get_input(char *buf,int *historycount,char history[100][256]) {
    int count = 0;
    int i;
    int len;
    char  *temp;
    char *msg;
    char str[256];

    msg = (char *)malloc(200);
    getcwd(msg,100);
    temp = (char* )malloc(sizeof(char) * 256);  
    memset(temp,0,sizeof(temp));  
    sprintf(str,"myshell$$ \033[;34m%s/\033[0m",msg);
    //简单处理界面,获得当前目录

    temp = readline(str);        
    strcpy(buf, temp);         
    //readline会打印里面的字符串,然后接受所输入的字符串

    len = strlen(buf);           
    for(i = 0;i < len; i++) {
        if(buf[i] == ' '|| buf[i] == '\n') 
            count++;
    }
    //统计空格和换行

    //如果直接空格或换行不存储命令
    if(count != len) {
        if(*historycount == 0){
                strcpy(history[*historycount],buf);
                *historycount = *historycount + 1;
                add_history(buf);
        } else {
            //如果前后两次命令相同,不再存储
            if(strcmp(history[*historycount-1],buf) != 0){
                strcpy(history[*historycount],buf);
                *historycount = *historycount + 1;
                add_history(buf);//将命令存储在历史列表,可以用上下键来进行操作
            }
        }
    }

    free(temp);
}

//解释输入,将输入以空格为界线分开,argcount存储命令个数,arglist存储具体命令
void explain_input(char *buf,int *argcount,char arglist[100][256]) {
    int number = 0 ;
    char *p = buf;
    char *q = buf;

    while(1){
        if(p[0] == '\n' || p[0] == 0)  //空格或者换行结束
            break;
        if(p[0] == ' ') //跳过空格
            p++;
        else{
            q = p;
            number = 0;
            while( (q[0] != ' ') && q[0] != 0  && q[0] != '\n'){
                number++; //代表单个命令的长度
                q++;    //移动到命令结尾
            }
            strncpy(arglist[*argcount],p,number+1);   //把单个命令解析出来
            arglist[*argcount][number] = 0;         //最后加上结尾
            *argcount = *argcount + 1;             //移动到下一个字符串 
            p = q;                                   //将p移动到末尾位置
        }
    }
}


//将命令分类,分别执行
void do_cmd(int argcount,char arglist[100][256],int historycount,char history[100][256]) {
    int flag = 0;
    int how = normal;
    int backgroud = not_background;
    int status;
    int i;
    int fd;
    char *arg[argcount + 4];
    char *argnext[argcount + 4];
    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){
                backgroud = 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 = double_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++;
        }
    }

    //只允许单个 > < >> |命令
    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 == double_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){
        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;
            }
        }
    }

  // 如果命令为history 则输出历史。
    if(strcmp(arg[0],"history") == 0&& argcount == 1){
        for(i = 0;i < historycount;i++){
            printf("%d\t %s\n",i+1,history[i]);
        }
        return;
    }


 //完成内嵌cd命令
    if(strcmp(arg[0],"cd") == 0 && argcount == 2){
        //处理特殊的cd或cd ~命令
        if(argcount == 1 || argcount == 2 && strcmp(arg[1],"~") == 0) {
            chdir("/home/yinjianxiang");
            return;
        }
        if(chdir(arg[1]) == -1){
            perror("chdir");
        }
        return;
    }

    //fork一个子进程,执行参数
    if( (pid = fork()) < 0){
        printf("fork error\n");
        return ;
    }

    switch(how){
        case normal:
            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 out_redirect:
            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 double_out_redirect:
            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 |O_APPEND,0644);//打开文件
                dup2(fd,1);                 //把输出从屏幕交给了文件
                execvp(arg[0],arg);
                close(fd);
                exit(0);
            }
            break;

        // <
        case in_redirect:
            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);         
                close(fd);  
                exit(0);    
            }
            break;

        // |
        case have_pipe:
            if(pid == 0){
                int pid2;
                int status2;
                int fd2;

                //fork执行|前面的命令
                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);
                    }

                    //将屏幕的输出流入文件之中
                    if((fd2 = open("/tmp/tempfile",O_WRONLY | O_CREAT | O_TRUNC ,0644)) == -1) {
                        printf("open /tmp/tempfile failed!");
                    }
                    dup2(fd2,1);
                    execvp(arg[0],arg);
                    close(fd2);
                    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);
                }

                if((fd2 = open("/tmp/tempfile",O_RDONLY)) == -1) {
                    perror("open");
                }
                //用文件代替键盘,执行第二条命令
                dup2(fd2,0);

                execvp(argnext[0],argnext);
                if(remove("/tmp/tempfile") == -1) {//移除文件
                    perror("remove");
                }
                exit(0);
            }
            break;

        default :
            break;
    }

    //杀死父进程,后台进行
    if(backgroud == 1 && pid != 0){
        printf("[process id %d]\n",pid);
        return;
    }

    //父进程等待子进程
    if(waitpid(pid,&status,0) == -1){
        printf("wait for child process error!\n");
    }


}


static int find_command(char *command) {
    DIR *dp;
    struct dirent *dirp;
    char *path[] = {"./","/bin","/usr/bin",NULL};

    //使当前目录下可执行文件能够执行
    if(strncmp(command,"./",2) == 0) {
        command = command + 2;
    }


    int i = 0;
    //在当前目录,/bin和/usr/bin中执行
    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;    //返回1
            }
        }
        closedir(dp);
        i++;  //去找下一个目录
    }
    return 0; //没找到
}

#endif

makefile

cc = gcc
OBJ = myshell
all :
    $(cc)  -c  ./*.c
    $(cc)  -o $(OBJ)  ./*.o  -I /usr/lib/x86_64-linux-gnu/libreadline.so  -lreadline -ltermcap  -g
    rm -rf  *.o
clean:
    rm -rf *.o
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值