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

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

相关推荐
鱼鳞_2 小时前
Java学习笔记_Day21
java·笔记·学习
码云数智-大飞2 小时前
Java异常体系深度解析:驾驭Checked与Unchecked的艺术
java
fengci.3 小时前
polar2026年春季个人挑战赛(WEB 困难部分)
java·大数据·elasticsearch
Sean‘3 小时前
Linux系统下安装Trivy
linux·运维·服务器
攒了一袋星辰3 小时前
SequenceGenerator废弃序列号异步补偿机制技术实现方案
java·开发语言·数据库·mysql
amIZ AUSK3 小时前
Spring Boot 自动配置
java·spring boot·后端
努力学习的小廉3 小时前
Python 零基础入门——基础语法(一)
java·网络·python
上海运维Q先生3 小时前
SUSE 报错修复:unable to open display ““
linux·运维
AI精钢3 小时前
Claude Certification 出现了一道“官方文档级”错题:关于 Claude Code Skills 优先级的误导
java·开发语言·工程实践·claude code·ai coding·agent skills·技术认证
云栖梦泽3 小时前
Linux内核与驱动:5.并发与竞争
linux·c++