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

基于Linux下的MyBash命令解释器

项目介绍:⼀个⽤C语⾔实现的简单shell,可以接受⽤⼾输⼊的命令并执⾏操作,⽀持多管道和重 定向。

mybash---打造自己的命令解释器
目前我们Linux的系统默认的命令解释器是bash; 
命令解释器(也称为命令行解释器或shell)是计算机操作系统中的一个重要组件,它负责接收用户输入的命令,并解释和执行这些命令。其实命令解释器就是解析命令,执行命令,输出反馈;

1.命令的分类

内置命令和普通命令
1.内置命令:cd exit
2普通命令:ls pwd cp ps   等等
如果是普通命令,那么使用which是可以找到的,比如which ps;which ls;which pwd;which cp;
也就是普通命令是一个可执行程序.
但是我们找cd和exit是找不到的;  因为内置命令cd,exit等它是在bash本身实现的;
而bash也是一个可执行程序,比如:which bash;
简单来讲,就是普通命令是通过fork+exec实现的;而内置命令是bash自身通过调用相应的接口实现的;

2.项目框架

3.strtok的介绍

字符串分割函数

注意:
strtok线程不安全,原因就是函数实现使用了一个static的变量(指针记录下次分割的地址,再次调用要沿用上次的,所以需要静态变量).
在多线程中,如果两个线程都使用了strtok的话,这个变量的值就会被另一个线程不定期的进行修改. 

4.mybash.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string.h>

#define  ARG_MAX  10
char *get_cmd(char *buff,char *myargv[])
{
    if(buff==NULL||myargv==NULL)
    {
        return NULL;
    }

    int i=0;
    char *s=strtok(buff," ");
    while(s!=NULL)
    {
        myargv[i++]=s;
        s=strtok(NULL," ");
    }

    return myargv[0];
}
int main()
{
    while(1)
    {
        printf("stu@loalhost:~$");
        fflush(stdout);

        char buff[128];
        fgets(buff,128,stdin);//ls  cd 路径名    cp  a.c  b.c

        char *myargv[ARG_MAX]={0};
        buff[strlen(buff)-1]='\0';//注意这一句,自己练习一下调试

        char *cmd=get_cmd(buff,myargv);//得到命令cmd和它的参数(cmd和参数一起放在了myargv)

        if(cmd==NULL)
        {
            continue;
        }
        else if(strcmp(cmd,"cd")==0)
        {
           //
        }
        else if(strcmp(cmd,"exit")==0)
        {
           // exit(0);
           break;
        }
        ///

        else
        {
            //普通命令
            //fork+exec
        }
    }
    //
    exit(0);
}

 代码效果只能显示出命令,并不能执行命令

5.进行进一步的修改

 对具体函数进行填充

int main()
{
    while(1)
    {
    //    printf("stu@localhost        ~$");
        printf_info();
        //
        fflush(stdout);

        char buff[128]={0};
        fgets(buff,128,stdin);//ls,ps -f,cp a.c b.c
        buff[strlen(buff)-1]='\0';

        char *myargv[ARG_MAX]={0};
        char *cmd=get_cmd(buff,myargv);

       if(cmd==NULL)
         {
             continue;
        }
       else    if(strcmp(cmd,"cd")==0)
        {
             //...
             if(myargv[1]!=NULL)
             {
               if(chdir(myargv[1])==-1)
               {
                 perror("cd err!\n");
               }
             }
        }
        else if(strcmp(cmd,"exit")==0)
        {
            //exit(0);//OK,不建议
            break;
        }
        else
        {
            //fork+exec;
            run_cmd(cmd,myargv);
        }
    }

    //...
    exit(0);
}
void  printf_info()
{
    char *user_str="$";
    int user_id=getuid();
    if(user_id==0)
    {
        user_str="#";
    }

    struct passwd *ptr=getpwuid(user_id);
    if(ptr==NULL)
    {
        printf("mybash1.0>>  ");
        fflush(stdout);
        return ;
    }
    char hostname[128]={0};
    if(gethostname(hostname,128)==-1)
    {
        printf("mybash1.0>>  ");
        fflush(stdout);
        return ;
    }
    char dir[256]={0};
    if(getcwd(dir,256)==NULL)
    {
        printf("mybash1.0>>  ");
        fflush(stdout);
        return ;
    }

    printf("\033[1;32m%s@%s\033[0m  \033[1;34m %s\033[0m%s ",ptr->pw_name,hostname,dir,user_str);
    fflush(stdout);
}

一次对用户id ,主机名,当前目录初始化和定义 更改后效果

现在我们完善普通命令:

void run_cmd(char *path,char *myargv[])
{
    if(path==NULL ||myargv==NULL)
    {
        return ;
    }
    pid_t pid=fork();
    if(pid==-1)
    {
        return ;
    }
    if(pid==0)
    {
       execvp(path,myargv);
       perror("execvp   error!\n");
       exit(0);
    }

    else
     {
         wait(NULL);
     }
  }

运行结果如下: 

 实际上应用了exevp函数,让操作系统自动调用了 /user/bin 文件里面的 各个普通命令实现mybash

完成一个借鸡生蛋的过程

6.我们也可以写出自己的二级制可执行程序,实现真正的mybash

例如 clear和pwd:

//clear.c
 #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    printf("\033[2J\033[0;0H");
}

    
    
//pwd.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    char path[256]={0};
    if(getcwd(path,256)==NULL)
    {   
        perror("getcwd  error!\n");
        exit(1);
    }   

    printf("%s\n",path);
    exit(0);
}

 并且更改路径

还应PATH_BIN的位置

#define PATH_BIN "/home/stu/quzijie/test15/mybin/"

这是我自己文件地址

void run_cmd(char *path,char *myargv[])
{
    if(path==NULL ||myargv==NULL)
    {
        return ;
    }
    pid_t pid=fork();
    if(pid==-1)
    {
        return ;
    }
    if(pid==0)
    {
      // execvp(path,myargv);
      char pathname[128]={0};
      if(strncmp(path,"/",1)==0||strncmp(path,"./",2)==0)
      {
          strcpy(pathname,path);
      }
      else
      {
          strcpy(pathname,PATH_BIN);
          strcat(pathname,path);
      }
      execv(pathname,myargv);
       perror("execvp   error!\n");
       exit(0);
    }

 结果如下:

 7.总结

基于Linux内核的命令解释程序(Shell),设计了⼀个⾃⼰的MyBash命令解释器。实现 了⼤致的程 序总框架,系统命令提⽰符显⽰,对⽤⼾输⼊命令的解析和执⾏,其中区别了系统的内 置命令和外置命令。具体实现了:显⽰指定⽬录下⽂件列表ls 和ls的部分携带参数例如“-l”“-a”、 显⽰当前位置的绝对路径pwd、清除 clear以及exit

相关文章:

  • 【13】Ajax爬取案例实战
  • 通过Docker快速搭建VoceChat | 开源轻量自托管聊天工具
  • 基于Spring Boot的网上商城系统的设计与实现(LW+源码+讲解)
  • 213.SpringSecurity:授权,授权实战,OAuth2,SpringSecurity中OAuth2认证服务器、资源服务器搭建,JWT
  • Oracle 19C 备份
  • vue3中<script setup>语法糖是什么意思。为什么叫语法糖,为什么叫糖,它甜吗
  • vue2前端日志数据存储(indexedD)自动清理3天前的数据
  • 数据结构初阶-二叉树链式
  • el-input表单校验只能输入数字格式的数据
  • 火山引擎云上实战: DeepSeek R1 大模型(全尺寸)
  • 把手搭建vue前后端管理系统-TAB标签通过pinia来进行管理(二十六)
  • [特殊字符] 校园外卖跑腿平台源码技术解析与实战搭建指南
  • 鸡生蛋还是蛋生鸡? 基于python的CCM因果关系计算
  • ROS2的发展历史、核心架构和应用场景
  • 【机器学习】使用Python Spark MLlib进行预测模型训练
  • ChatDBA VS DeepSeek:快速诊断 OceanBase 集群新租户数据同步异常
  • GPU架构与通信互联技术介绍
  • 如何使用Tailwind CSS创建一个组合了很多样式的类名,实现样式复用
  • 【概念】Node.js,Express.js MongoDB Mongoose Express-Validator Async Handler
  • [ComfyUI] SDXL Prompt Styler 自定义节点的作用解析
  • 金砖国家外长会晤主席声明(摘要)
  • 安徽省公安厅原副厅长刘海石主动投案,正接受审查调查
  • 总有黑眼圈是因为“虚”吗?怎么睡才能改善?
  • 一季度我国服务进出口总额19741.8亿元,同比增长8.7%
  • 第二艘国产大型邮轮实现坞内起浮,重点强化邮轮供应链本土化建设
  • 普京发表声明感谢协助俄军收复库尔斯克州的朝鲜军人