【Linux小项目】实现自己的bash

0. bash原理介绍

bash实际上就是一个负责解析输入字符串工具.

我们需要做的事是这些:

  1. 手动分割出输入的字符串
  2. 判断哪些变量是内建命令(自己执行),哪些命令是普通命令(创建子进程执行)
  3. 实现的功能有: echo export cd 常规指令 输入、输出流重定向
cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include<fcntl.h>

#define HOST_NAME "hecs-225896"
#define LINE_SIZE 128
#define DELIM " "
#define SHEEL_COMMAND 0
#define NORMAL_COMMAND 1
#define IN_RESTREAM 0
#define OUT_RESTREAM 1
#define APPEND_RESTREAM 2 
#define NONE 0

char pwd[LINE_SIZE];
char command[LINE_SIZE];
char* _argv[LINE_SIZE];
char myenv[LINE_SIZE];
int lastcode;
char * filename;
int restream = NONE;
int stream = 0;


char * getpwd()
{
    return getcwd(pwd,sizeof(pwd));
}
char* getusr()
{
    return getenv("USER");
}
void interactive()
{
    char symbol;
    if(!strcmp(getenv("USER"),"root"))
        symbol='#';
    else
        symbol='$'; 
    printf("%s@"HOST_NAME":""%s""%c ",getusr(),getpwd(),symbol);
    fgets(command,sizeof(command)-1,stdin);
    //消除'\n'
    command[strlen(command)-1]='\0';
    // printf("%s",command);
}
int split()
{
    for(int i=0;command[i];i++)
    {
        if(command[i]=='>')  //写入重定向
        {
            
            command[i++]='\0';
            restream=OUT_RESTREAM;
            if(command[i]=='>') //追加重定向
            {
                restream=APPEND_RESTREAM;
                command[i++]='\0';
                while(command[i]==' ')i++;
                filename=command+i;
                printf("filename:%s\n",filename);
                stream=open(filename,O_CREAT|O_APPEND,0666);
            }
            else
            {
                while(command[i]==' ')i++;
                filename=command+i;
                printf("filename:%s\n",filename);
                stream=open(filename,O_CREAT|O_WRONLY|O_TRUNC,0666);
            }
            break;

        }
        else if(command[i]=='<')
        {
            restream=IN_RESTREAM;
            command[i++]='\0';
            while(command[i]==' ')i++;
            filename=command+i;
            printf("filename:%s\n",filename);
            stream=open(filename,O_RDONLY);
            break;
        }
    }
    int _argc=0;
    _argv[_argc++]=strtok(command,DELIM);
    while(_argv[_argc]=strtok(NULL,DELIM))
    {
        _argc++;
    }
    _argv[_argc]=NULL;
    return _argc;
}
int JudgeCommand()
{
    if(!strcmp(_argv[0],"cd"))
    {
        return SHEEL_COMMAND;
    }
    else if(!strcmp(_argv[0],"echo"))
    {
        return SHEEL_COMMAND;
    }
    else if(!strcmp(_argv[0],"export"))
    {
        return SHEEL_COMMAND;
    }
    return NORMAL_COMMAND;
}
int execute_NormalCommand()
{
    
    pid_t id=fork();
    if(id<0)
    {
        perror("fork faild\n");
        return 1;
    }
    else if(id==0)
    {
        if(restream == OUT_RESTREAM || restream == APPEND_RESTREAM )
        {
            dup2(stream,1);
        }
        else if(restream == IN_RESTREAM)
        {
            dup2(stream,0);
        }
        execvp(_argv[0],_argv);
        exit(2);
    }
    else if(id>0)
    {
        int status=0;
        pid_t wid=waitpid(id,&status,0);
        if(wid==id)
            lastcode = WEXITSTATUS(status);
    }
}
int execute_ShellCommand(int argc)
{
    if(argc>=2&&!strcmp(_argv[0],"cd"))
    {
        int rev=chdir(_argv[1]);
        if(rev!=0)
        {
            perror("return faild");
        }
    }
    else if(argc>=2&&!strcmp(_argv[0],"echo"))
    {
        if(_argv[1][0]=='$')
        {
            printf("%c",_argv[1][0]);
            char * env=getenv(_argv[1]+1);
            if(env)printf("%s\n",env);
        }
        else if(!strcmp(_argv[1],"?"))
        {
            printf("%d\n",lastcode);
            lastcode=0;
        }
        else
            printf("%s\n",_argv[1]);
    }
    else if(argc>=2&&!strcmp(_argv[0],"export"))
    {
        strcpy(myenv,_argv[1]);
        putenv(_argv[1]);
    }
    
    
}
void BuildCommand(int *_argc)
{
    if(!strcmp(_argv[0],"ls"))
    {
        _argv[(*_argc)++]="--color";
        _argv[*_argc]=NULL;
    }
}

int main()
{
    while(1)
    {
        interactive();
        int argc=split();
        if(argc==0)continue;
        // for(int i=0;i<argc;i++)printf("%s\n",_argv[i]);
        int RunFlag=JudgeCommand();
        BuildCommand(&argc);
        if(RunFlag==NORMAL_COMMAND)
        {
            execute_NormalCommand();
        }
        else{
            execute_ShellCommand(argc);
        }
    }
}
相关推荐
小字节,大梦想几秒前
【Linux】重定向,dup
linux
blessing。。1 小时前
I2C学习
linux·单片机·嵌入式硬件·嵌入式
2202_754421541 小时前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
运维&陈同学2 小时前
【zookeeper03】消息队列与微服务之zookeeper集群部署
linux·微服务·zookeeper·云原生·消息队列·云计算·java-zookeeper
周末不下雨3 小时前
win11+ubuntu22.04双系统 | 联想 24 y7000p | ubuntu 22.04 | 把ubuntu系统装到1T的移动固态硬盘上!!!
linux·运维·ubuntu
哎呦喂-ll4 小时前
Linux进阶:环境变量
linux
Rverdoser4 小时前
Linux环境开启MongoDB的安全认证
linux·安全·mongodb
PigeonGuan4 小时前
【jupyter】linux服务器怎么使用jupyter
linux·ide·jupyter
东华果汁哥4 小时前
【linux 免密登录】快速设置kafka01、kafka02、kafka03 三台机器免密登录
linux·运维·服务器