当前位置: 首页 > article >正文

linux中实现自己的bash

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

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

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

🚀🚀🚀大家觉不错的话,就恳求大家点点关注,点点小爱心,指点指点🚀🚀🚀

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

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

        那就让我们一起来探索属于自己自己的bash 

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

源码 

#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;
}

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


http://www.kler.cn/a/136912.html

相关文章:

  • 解决 vue3 中 echarts图表在el-dialog中显示问题
  • 解线性方程组
  • 本原多项式
  • 虚拟机桥接模式网络连接不上解决方法
  • 第十七届山东省职业院校技能大赛 中职组“网络安全”赛项任务书正式赛题
  • 编程语言的软件工程
  • linux内核管理
  • Redis篇---第十二篇
  • OpenAI政变背后是科学家创始人的悲歌
  • 一阶低通滤波器(一阶巴特沃斯滤波器)
  • ⑩⑦【MySQL】锁:全局锁、表级锁、行级锁
  • 「Verilog学习笔记」实现3-8译码器①
  • 亚马逊车灯外贸出口CE认证标准办理解析
  • vite vue3配置axios
  • DefaultMQPushConsumer的整体流程
  • windows11系统如何设置锁屏壁纸
  • 中石油勘探院张弢:从业务到架构全面探讨中国石油的数字化转型之路
  • 认识.NET Aspire:高效构建云原生应用的利器
  • Java-类和类的关系
  • 如何用html css js 画出曲线 或者斜线;
  • Flink(六)【DataFrame 转换算子(下)】
  • 一篇文章让你彻底了解Java算法「十大经典排序算法」
  • 〖大前端 - 基础入门三大核心之JS篇㊲〗- DOM改变元素节点的css样式、HTML属性
  • fast lio 2 保存每一帧的点云PCD和里程计矩阵 Odom 在txt文件
  • 深入探索 PaddlePaddle 中的计算图
  • Wireshark的数据包它来啦!