实现一个my_shell

实现的功能

  1. 实现 输入输出重定向 (< > >>)
  2. 实现 后台运行(&)
  3. 实现 内建命令(cd)
  4. 界面美观(上色)
  5. 屏蔽部分信号(如ctrl+c)
  6. 实现管道(|)
  7. 支持 使用Tab补全、光标移动
  8. 实现 内置命令history

这是对my_shell的一些新理解

1. 进程同步
2.安全的信号处理函数

List item

总结
基本功能其实都非常基础 就是在使用readline动态链接库的时候因为不熟悉出现了一些问题 其实库的使用非常简单 代码中非常清楚 参考即可
这是一篇不错的readline介绍博客

实现代码

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

#define normal       0   //正常
#define out_redirect 1   //输出重定向
#define in_redirect  2   //输入重定向
#define have_pipe    3   //有管道
#define out_redirect_acc  4 //累加输出重定向
char arglist[100][256];

void print_promt();
void get_input(char **buf);
void explain_input(char *,int *,char a[100][256]);
void do_cmd(int , char a[100][256]);
int find_command(char *);
/* char *readline (const char *prompt);//返回值就是读取的字符串
void add_history(const char *string);//用来返回历史
typedef char *rl_compentry_func_t(const char *text, int state);
char **rl_completion_matches(const char *text, rl_compentry_func_t *entry_func);  */
int background=0;  //判断是否存在后台运行符

void handler(int signo)
{
    char tmp[256];
    char tmpa[256];
    int tt=0;
    getcwd(tmp,256);
    int dd=strlen(tmp)-2;      //这段代码是提取出路径中的最后一个文件
    for(int i=dd;i>=0;i--)
    {
        if(tmp[i]=='/')
        {
            tt=i;
            break;
        }
    }
    for(int i=tt;i<=dd+1;i++) tmpa[i-tt]=tmp[i];
    tmpa[dd+2-tt]='\0'; 
    printf("\001\033[1m\002");
    printf("\033[34m");
    printf("\n错误!my_shell@%s$$:",tmp);
    printf("\001\033[0m\002");
    //setbuf(stdout,_IO_BUFSIZ);
}

void ShowHistory()
{
    int i = 0;
    HIST_ENTRY ** his;
    his = history_list();
    while(his[i] != NULL)
    printf("%s\n", his[i++]->line);
}

int my_cd(char *buf)
{
    if(chdir(buf)<0)
    return 0;
    else return 1;
}

int main(int argc,char **argv)
{
    system("clear");
    setbuf(stdout,NULL);       /*程序在接受到SIGINT信号时转入的
    处理函数不处理缓冲区会转入下一行 可以删除掉看看*/
    read_history(NULL);    //默认在~/.history
    int i=0;
    int argcount=0;
    char **arg=NULL;
    signal(SIGINT,handler);
    while(1){
        char *buf=(char*)malloc(256);
        if(buf==NULL)
        {
            perror("error in malloc\n");
            exit(1);
        }
        background=0;
        memset(buf,0,256);
        print_promt();
        get_input(&buf);
        if(!strcmp(buf,"exit") || !strcmp(buf,"logout")) break;
        for(i=0;i<100;i++) arglist[i][0]='\0';
        argcount=0;
        explain_input(buf,&argcount,arglist);
        if(!strcmp(buf,"history"))
        {
            ShowHistory();
            continue;
        }
/*         for(int i=0;i<argcount;i++)
        printf("%s\n",arglist[i]); */
        if(!strcmp(arglist[0],"cd"))
        {
            arglist[1][strlen(arglist[1])]='\0';
            if(my_cd(arglist[1]))
            {
                char tmp_file[256];
                getcwd(tmp_file,256);
                continue;
            }
            else
            {
                printf("param error!\n");
                continue;
            }
        }
        do_cmd(argcount,arglist);    
        free(buf);
    }
    exit(0);
}

void print_promt()
{
    setbuf(stdout,NULL);
    char tmp[256];
    char tmpa[256];
    int tt=0;
    getcwd(tmp,256);
    int dd=strlen(tmp)-2;
    for(int i=dd;i>=0;i--)
    {
        if(tmp[i]=='/')
        {
            tt=i;
            break;
        }
    }
    for(int i=tt;i<=dd+1;i++) tmpa[i-tt]=tmp[i];
    tmpa[dd+2]='\0';
    printf("\001\033[1m\002");
    printf("\033[34m");
    printf("\nmy_shell@%s$$:",tmpa);
    printf("\001\033[0m\002");
    //setbuf(stdout,_IO_BUFSIZ);
}

void get_input(char **buf)
{
    int len=0;
    char ch='\0';
/*     ch=getchar();
    while(len<256)
    {
        if(ch=='\n') break;
        buf[len++]=ch;
        ch=getchar();
    } */
    *buf=readline("my_shell@"); //不在这里加字符串可能会出现缓冲区问题 导致光标移动异常
    add_history(*buf);
    write_history(NULL);
    if(len==256)

    {
        perror("command is too long \n");
        return 0;
    }
}

void explain_input(char *buf,int *argcount,char arglist[100][256])
{
    char *q=buf;
    char *p=buf;
    int number=0;
    for(int i=0;i<strlen(buf);i++)
    if(buf[i]=='#')
    {
        background=1;  //判断是否存在后台运行符
    }
    while(1)
    {
        if(p[0]=='\0')
        break;
        if(p[0]==' ')
        p++;
        else
        {
            q=p;
            number=0;
            while(q[0]!='\0' && q[0]!=' '){
                q++;
                number++;
            }
            strncpy(arglist[*argcount],p,number+1);
            arglist[*argcount][number]='\0';//最后一项为'\0'
            *argcount=*argcount+1;
            p=q;
        }
    }
}

void do_cmd(int argcount,char arglist[100][256])
{
    int flag=0;
    int how=0;     //检测是否存在重定向 管道
    int status=0;
    int i=0;
    int fd=0;
    char *arg[argcount+1];
    char *argnext[argcount+1];
    char *file;
    pid_t pid=0;

    for(i=0;i<argcount;i++)
    {
        arg[i]=(char*)arglist[i];
    }
    arg[argcount]=NULL;
    for(i=0;arg[i]!=NULL;i++)
    {
        if(!strcmp(arg[i],">"))
        {
            flag++;
            how=out_redirect;
            if(arg[i+1]==NULL)
            flag++;     //错误的格式
        }
    }
    for(i=0;arg[i]!=NULL;i++)
    {
        if(!strcmp(arg[i],">>"))
        {
            flag++;
            how=out_redirect_acc;
            if(arg[i+1]==NULL)
            flag++;     //错误的格式
        }
    }
    for(i=0;arg[i]!=NULL;i++)
    {
        if(!strcmp(arg[i],"<"))
        {
            flag++;
            how=in_redirect;
            if(i==0)
            flag++;     //错误的格式
        }
    }  
    for(i=0;arg[i]!=NULL;i++)
    {
        if(!strcmp(arg[i],"|"))
        {
            flag++;
            how=have_pipe;
            if(arg[i+1]==NULL)
            flag++;     //错误的格式
            if(i==0)
            flag++;
        }
    } 
    if(flag>1)
    {
        perror("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)
    {
        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]; //其实就是NULL
                break;
            }
        }
    }
    if((pid=fork())<0)
    {
        printf("fork error\n");
        return;
    }
    switch (how)
    {
        case 0:     //没有什么参数
            if(pid==0)
            {
                if(!find_command(arg[0]))
                {
                    perror("can't find this command!\n");
                    exit(0);
                }
                execvp(arg[0],arg);   //子进程运行参数命令
                exit(0);              //退出子进程
            }
            break;
        case 1:    //输出重定向
            if(pid==0)
            {
                if(!find_command(arg[0]))
                {
                    perror("can't find this command!\n");
                    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 can 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=0;
                int fd2=0;
                int status=0;
                if((pid2=fork())<0)   //子进程再次分离一个子进程
                {
                    perror("error in fork\n");
                    exit(0);
                }
                else if(pid2==0)  //子进程
                {
                    if(!find_command(arg[0]))
                    {
                        printf("%s :Command can not found\n",arg[0]);
                        exit(1);
                    }
                    fd2=open("/tmp/youdontnofile",O_WRONLY|O_CREAT|O_TRUNC,0644);
                    dup2(fd2,1);
                    execvp(arg[0],arg);
                    exit(0);
                }
                if(waitpid(pid2,&status,0)==-1)  //第三个参数未被设置 所以阻塞父进程
                {
                    printf("wait child process error\n");
                    exit(1);
                }
                if(!find_command(argnext[0]))
                {
                    printf("%s :Command can not found\n",argnext[0]);
                    exit(1);
                }
                fd2=open("/tmp/youdontnofile",O_RDONLY);
                dup2(fd2,0);
                execvp(argnext[0],argnext);
                if(remove("/tmp/youdontnofile"))
                {
                    perror("error in remove\n");
                }
                exit(1);
            }
            break;
        case 4:       
            if(pid==0)
                {
                    if(!find_command(arg[0]))
                    {
                        perror("can't find this command!\n");
                        exit(0);
                    }
                    fd=open(file,O_RDWR|O_APPEND,0644); 
                    dup2(fd,1);
                    execvp(arg[0],arg);
                    exit(0);
                }
                break;
        default:
            break;
    }
    if(background==1)  //子进程不会走到这里来
    {
        return; //父进程直接返回
    }
    if(waitpid(pid,&status,0)<0)
    {
        perror("error in wairpid\n");
        exit(1);
    }
}

int find_command(char *command)
{
    DIR* dp;
    struct dirent* dirp;
    char *path[]={"./","/bin","/usr/bin","/home/lizhaolong/suanfa/TeamF/LZL/Requirement",NULL};
    //为了执行一些自己写的命令 所以加上了工作路径
    if(strncmp(command,"./",2)==0)
    {
        command=command+2;//指针向后移动
    }
    int i=0;
    while(path[i]!=NULL){
        if((dp=opendir(path[i]))==NULL)
        {
            perror("error in opendir\n");
            exit(1);
        }
        while((dirp=readdir(dp))!=NULL){
            if(!strcmp(dirp->d_name,command))
            {
                //printf("找到!\n");
                close(dp);
                return 1;
            }
        }
        close(dp);
        i++;
    }
    return 0;
}
  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李兆龙的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值