linux中实现自己的bash

🐶博主主页: @ᰔᩚ. 一怀明月ꦿ

❤️‍🔥专栏系列: 线性代数C初学者入门训练题解CC的使用文章「初学」C++

🔥座右铭: "不要等到什么都没有了,才下定决心去做"

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀
感觉,有很久都没有写博客,主要是最近学的内容难以理解,所以时间不太充足,就没有写博客的时间,今天为什么要这一篇文章呢?主要是我感觉实现一个自己的命令行小程序还是比较有趣的。

我们平时都是在linux的shell命令行上直接输入指令,有没有想过自己也可以去实现一个,自己的bash呢?

那就让我们一起来探索属于自己自己的bash
这次我就直接给出源代码,没有把其中的方法,单独拿出来分析,我主要觉得那样有点显得代码冗余,其实也不用担心看不懂,我在源码中加了很多注释,大家也不怕看不懂。其实这样做还有一个好处,就是我们可以直接复制到我们的linux文件中,直接运行。

源码

cpp 复制代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

#define SIZE 100 //用于定义命令的最大长度
#define NUM 1024 //用于定义命令参数的最多个数
#define SEP " " //分割字符的时候,默认以空格作为分隔符

char _cwd[1024]; //用于存储PWD的环境变量
char env_val[1024]; //用于自定义一个环境变量,这里有一个缺点,就是env_val是一个数组,只能添加一个自定义的环境变量,下次添加时,就会覆盖上次添加的环境变量
int lastcode=0; //用于定义最近一个进程的退出码

//获取用户名字
const char* getUsername(void)
{
    const char* name=getenv("HOME");//getenv如果没有获取到环境变量会返回空值
    if(name)return name;
    else return "none";
}

//获取主机名字
const char* getHostname(void)
{
    const char* host=getenv("HOSTNAME");
    if(host)return host;
    else return "none";
}

//获取工作目录
const char* getCwd(void)
{
    const char* cwd=getenv("PWD");
    if(cwd)return cwd;
    else return "none";
}

//获取用户输入的命令
int getUserCommand(char* usercommand,int num)
{
    char* r=fgets(usercommand,num,stdin);
    if(r==NULL)
        return -1;
    usercommand[strlen(usercommand)-1]='\0';
    return strlen(usercommand);
}

//分割字符串
int commandSplist(char* usercommand,char* argv[])
{
    int argc=0;
    argv[argc++]=strtok(usercommand,SEP);
    while(argv[argc++]=strtok(NULL,SEP));
    return 0;
}

//执行命令
int execute(char* argv[])
{
    pid_t id=fork();
    if(id<0)return -1;
    else if(id==0)
    {
        //child
        execvp(argv[0],argv);//程序替换
        exit(1);//程序替换失败返回1
    }
    else
    {
        //farther
        int status=0;//保存子进程退出时的退出码和退出信号
        pid_t rid=waitpid(id,&status,0);//阻塞等待
        if(rid>0)
            lastcode=WEXITSTATUS(status);//获取子进程退出时的退出码
    }
    return 0;
}

//改变工作路径
void cd(const char* path)
{
    chdir(path);//改变工作路径
    //虽然路径改了,但是环境变量中PWD存储的工作路径并没有改变
    char temp[1024];
    getcwd(temp, sizeof(temp));
    sprintf(_cwd, "PWD=%s",temp);
    putenv(_cwd);//为什么这里putenv[temp]?因为temp是一个临时变量,putenv只是将temp这个指针放到环境变量体系中,当函数调用结束,指针就销毁了,这个工作路径的环境变量就访问不了了
    
}

//什么叫做内建命令:内建命令就是bash自己执行的,类似于自己内部的一个函数!
//1.是内建命令 0不是内建命令
//执行的是内建命令
int doBuildin(char* argv[])
{
    if(strcmp(argv[0],"cd")==0)//cd命令
    {
        char* path=NULL;
        if(argv[1]==NULL)
            path=".";
        else
            path=argv[1];
        cd(path);
        return 1;
    }
    else if(strcmp(argv[0],"export")==0)//export命令
    {
        if(argv[1]==NULL)return 1;
        strcpy(env_val,argv[1]);
        putenv(env_val);
        return 1;
    }
    else if(strcmp(argv[0],"echo")==0)//echo命令
    {
        char val_0=*argv[1];
        char* val=argv[1]+1;//argv[1]+1:例如$? 则argv[1]是$ argv[1]+1是?
        if(val_0=='$'&&strcmp(val,"?")==0)//我们定义?保存着最近一个进程的退出码
        {
            printf("%d\n",lastcode);
            lastcode=0;
        }
        else if(val_0=='$')
        {
            printf("%s\n",getenv(val));//打印环境
        }
        else//echo打印字符
        {
            printf("%s\n",argv[1]);
        }
        return 1;
    }
    else
    {
        //可以添加其他的内建命令
    }
    return 0;
}

int main()
{
    while(1)
    {
        char usercommand[SIZE];//存储用户输入的命令
        char* argv[NUM];//存储命令行参数
        
        //打印命令行提示符
        printf("[%s@%s %s]$",getUsername(),getHostname(),getCwd());
        
        //输入命令
        int n=getUserCommand(usercommand, sizeof(usercommand));
        if(n<=0)continue;
        
        //分割字符
        commandSplist(usercommand, argv);
        
        //判断是否是内建命令
        n=doBuildin(argv);
        if(n==1)continue;
        
        //执行命令
        execute(argv);
    }
    return 0;
}

🌸🌸🌸如果大家还有不懂或者建议都可以发在评论区,我们共同探讨,共同学习,共同进步。谢谢大家! 🌸🌸🌸

相关推荐
罗伯特祥3 分钟前
C调用gnuplot绘图的方法
c语言·plot
Jackey_Song_Odd18 分钟前
解决Ubuntu下无法装载 Windows D盘的问题
linux·ubuntu
乔巴不是狸猫36 分钟前
第11周作业
linux
黑胡子大叔的小屋38 分钟前
基于springboot的海洋知识服务平台的设计与实现
java·spring boot·毕业设计
ThisIsClark41 分钟前
【后端面试总结】深入解析进程和线程的区别
java·jvm·面试
嵌入式科普1 小时前
嵌入式科普(24)从SPI和CAN通信重新理解“全双工”
c语言·stm32·can·spi·全双工·ra6m5
雷神乐乐2 小时前
Spring学习(一)——Sping-XML
java·学习·spring
Bessssss2 小时前
centos权限大集合,覆盖多种权限类型,解惑权限后有“. + t s”问题!
linux·运维·centos
小林coding2 小时前
阿里云 Java 后端一面,什么难度?
java·后端·mysql·spring·阿里云
V+zmm101342 小时前
基于小程序宿舍报修系统的设计与实现ssm+论文源码调试讲解
java·小程序·毕业设计·mvc·ssm