【linux课设】自主实现shell命令行解释器

shell和bash的关系

shell是命令解释器,它接收用户的命令并将其传递给内核去执行。bash,即GNU Bourne-Again Shell,是shell的一种实现方式,也是大多数linux系统下默认的shell。

bash的原理

大多数的指令进程(除了内建命令)都是bash的子进程。当我们要执行一条类似ls -a指令时,bash会提前fork出一个子进程,然后让子进程去执行指令。这是我们进程程序替换的思想。当然,中间的过程涉及到进程创建、虚拟内存、进程替换的细节,本篇文章不做叙述,感兴趣的可以去看我之前的博客,希望能对你有帮助。

我们可以画出bash进程执行指令的过程图来帮助理解:
在这里插入图片描述

在上图中,bash几乎一直在循环做以下动作:

1.获取指令
2.解析命令行
3.fork创建子进程
4.命令程序替换子进程
5.等待子进程终止

既然知道了bash的基本原理,我们同样也可以模拟以上动作来写一个mini版的shell

代码实现

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<string.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<errno.h>

#define SIZE 100
#define COMMAND_SIZE 200
#define ZEOR '\0'
#define ARGV_NUM 32
#define SKIP " "
#define SkipPath(p) do{p+=strlen(p)-1; while(*p!='/'){p--;}}while(0)

char* gArgv[ARGV_NUM];//指令参数
char cwd[SIZE*2];//当前路径的环境变量键值对
int lastcode=0;

const char* GetUserName(){//获取环境变量中的值
  const char* name=getenv("USER");
  if(name==NULL){
    return "None";
  }
  return name;
}

const char* GetHome(){//获取家目录
  const char* home=getenv("HOME");
  if(home==NULL){
    return "/";
  }
  return home;
}

const char* GetHostHome(){//获取主机名
  const char* hostname=getenv("HOSTNAME");
  if(hostname==NULL){
    return "None";
  }
  return hostname;
}

const char* GetCwd(){//获取当前路径
  const char* cwd=getenv("PWD");
  if(cwd==NULL){
    return "None";
  }
  return cwd;
}


//输出命令行
void MakeCommandLineAndPrint(){
    char line[SIZE];
    const char* name=GetUserName();
    const  char* hostname=GetHostHome();
    const char* cwd=GetCwd();
    SkipPath(cwd);
    snprintf(line,sizeof(line),"[%s@%s %s]> ",name,hostname,cwd=strlen(cwd)==1?"/":cwd+1);
    printf("%s",line);
    fflush(stdout);
}
//获取用户命令行
int GetUserCommand(char command[],size_t n){
  char* s=fgets(command,n,stdin);
  if(s==NULL)return -1;
  command[strlen(command)-1]=ZEOR;
  return (int)strlen(command);
}


//命令行分割,获取命令行参数列表
void SplitCommand(char command[],size_t n){
   gArgv[0]=strtok(command,SKIP);
   size_t index=1;
   char* t=gArgv[0];
   while(t!=NULL){
     t=strtok(NULL,SKIP);
      gArgv[index++]=t;
   }
   //size_t i=0;
   //for(;i<index;i++){
   //  printf("%s\n",gArgv[i]);
   //}
}

//cd内建命令
void Cd(){
  const char* path=gArgv[1];
  if(path==NULL){
    path=GetHome();
  }
  //更新当前工作目录
  chdir(path);//修改当前进程的工作路径

  //更新环境变量
  char t[SIZE*2];
  getcwd(t,sizeof(t));
  snprintf(cwd,sizeof(cwd),"PWD=%s",t);
  putenv(cwd);
}

//查看是否是内建命令
int CheckBuiltIn(){
  int yes=0;
  const char* cmd=gArgv[0];
  if(strcmp(cmd,"cd")==0){
    yes=1;
    Cd();
  }
  return yes;
}
// 处理创建子进程失败
void Die(){
  exit(1);
}
//执行指令
void ExeCommand(){ 
   pid_t id=fork();
   if(id<0){
     Die();
   }
   if(id==0){
     //child
     execvp(gArgv[0],gArgv);
     exit(errno);
   }else{
     int status=0;
     pid_t res=waitpid(id,&status,0);
     if(res>0){
       lastcode=WEXITSTATUS(status);//获取子进程退出码信息
       if(WIFEXITED(status)){
        if(lastcode!=0) printf("%s:%s:%d\n",gArgv[0],strerror(lastcode),lastcode);
       }
     }
   }
}

int main(){
  int quit=0;
  while(!quit){
     //1.输出一个命令行
     MakeCommandLineAndPrint();
    
     //2.获取用户命令
     char command[COMMAND_SIZE];
     int n= GetUserCommand(command,sizeof(command));
     if(n<=0)return 1;//输入指令不合法
    // printf("%s\n",command);

    //3.命令行字符串分割
     SplitCommand(command,sizeof(command));
    
     //4.查看命令是否是内建命令
     int flag=CheckBuiltIn();
     if(flag)continue;
     
     //5.执行指令
     ExeCommand();
  }
  return 0;
}

运用进程创建,进程替换的原理,基本模拟了shell解释命令的过程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/501206.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【如何解决一些常见的 Composer 错误的保姆级讲解】

&#x1f308;个人主页:程序员不想敲代码啊&#x1f308; &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家&#x1f3c6; &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提…

【企业动态】成都九号诶艾科技有限公司基本信息

成都九号诶艾科技有限公司基本情况概述 公司基本信息 成都九号诶艾科技有限公司成立于2023年7月4日,位于中国(四川)自由贸易试验区成都高新区。该公司主要从事人工智能应用软件开发、人工智能基础软件开发以及互联网数据服务等相关业务。此外,九号诶艾科技还涉足旅游开发项…

如何撰写研究论文

SEVENTYFOUR/SHUTTERSTOCK 即使对于有经验的作家来说&#xff0c;将数月或数年的研究浓缩到几页纸中也是一项艰巨的任务。作者需要在令人信服地解决他们的科学问题和详细地呈现他们的结果之间找到最佳平衡点&#xff0c;以至于丢失了关键信息。他们必须简明扼要地描述他们的方…

Android开发 OCR:通过Tesseract实现图片文字识别

下面是整个详解步骤过程 效果图一、OCR的含义二、什么是Tesseract三、前提准备1、添加依赖2、数据文件下载路径 四、实际代码案例Demo如下&#xff1a;Main.xmlMain.java 效果图 流程&#xff1a;获取assets中的图片显示到页面&#xff0c;提取照片内的文字 一、OCR的含义 o…

YoloV8实战:使用YoloV8检测钢材表面缺陷

摘要 本文讲解一下实战,使用YoloV8检测钢材表面缺陷。起因是有粉丝搞不定这个数据集,检测的时候出现了不收敛的情况。所以,给大家讲讲如何去做。 不收敛的原因 一般遇到不收敛的情况,就是数据转化的时候出错了,大家可以打开runs下面的训练日志,观察一下是否正常。 如…

揭秘谷歌Gemini Pro 1.5:如何免费体验处理超长对话的AI模型?

最近Google发布大模型API&#xff0c;让人有点想哭的那种。 他们发布了Gemini Pro&#xff0c;这个东西的免费key每分钟能调用60次&#xff01; 想想看&#xff0c;这速度&#xff0c;比GPT-3.5以前的免费key快了30倍不止。 而且&#xff0c;更厉害的是&#xff0c;即使是Ge…

C语言中的文件和文件操作

目录 为什么会有文件&#xff1f; 文件名 ⼆进制⽂件和⽂本⽂件&#xff1f; ⽂件的打开和关闭 流 标准流 ⽂件指针 ⽂件的打开和关闭 顺序读写函数介绍 对⽐⼀组函数&#xff1a; 文件的随机读写 fseek ftell rewind ⽂件读取结束的判定 被错误使⽤的 feof ⽂件…

TouchGFX之文本区域

文本区域在屏幕上显示文本。 文本区域的文本在大小、颜色、自定义字体、动态文本等方面是完全可以配置的 #ifndef TOUCHGFX_TEXTAREA_HPP #define TOUCHGFX_TEXTAREA_HPP #include <touchgfx/Font.hpp> #include <touchgfx/TextProvider.hpp> #include <touchg…

软件设计不是CRUD(16):低耦合模块设计理论——行为抽象与设计模式(下)

(接上文《软件设计不是CRUD(15):低耦合模块设计理论——行为抽象与设计模式(中)》) 3.2.4、之前的业务逻辑需要关注后续逻辑的执行成败,并调整自身执行的情况 这个场景在之前场景的基础上增加了新的控制要求,具体来说就是之前已经完成的控制逻辑执行,需要在后续控制…

Vue基础配置、组件通信

基础配置 Vue框架已经集成了webpack配置 小注意点 vbase 快速生成vue模板 组件名必须是多词格式(驼峰模式) 具体三种写法: ①小驼峰:abcDef.vue ②大驼峰&#xff1a;AbcDef.vue ③中横线&#xff1a;abc-def.vue 假如文件名不符合多次格式的补救办法&#xff1a; 导出重命名…

MySQL数据库MHA高可用集群

前言 MySQL 数据库 MHA&#xff08;Master High Availability&#xff09;高可用集群是一种用于提高 MySQL 数据库可用性的解决方案。它通过自动故障切换和监控来确保数据库系统在主服务器发生故障时能够快速切换到备用服务器&#xff1b;在 MHA 高可用集群中&#xff0c;Mast…

线程与同步

目录 一&#xff0c;线程概念 1&#xff0c;什么是线程 2&#xff0c;Linux 下的线程 3&#xff0c;POSIX 线程库 二&#xff0c;线程同步 1&#xff0c;数据不一致问题 2&#xff0c;如何解决这类问题 3&#xff0c;死锁 三&#xff0c;线程同步的运用 1&#xff0c…

第十四届蓝桥杯省赛C++ B组所有题目以及题解(C++)【编程题均通过100%测试数据】

第一题《日期统计》【枚举】 【问题描述】 小蓝现在有一个长度为100的数组&#xff0c;数组中的每个元素的值都在0到9的范围之内。数组中的元素从左至右如下所示&#xff1a; 5 6 8 6 9 1 6 1 2 4 9 1 9 8 2 3 6 4 7 7 5 9 5 0 3 8 7 5 8 1 5 8 6 1 8 3 0 3 7 9 2 7 0 5 8 8 …

【Go】五、流程控制

文章目录 1、if2、switch3、for4、for range5、break6、continue7、goto8、return 1、if 条件表达式左右的()是建议省略的if后面一定要有空格&#xff0c;和条件表达式分隔开来{ }一定不能省略if后面可以并列的加入变量的定义 if count : 20;count < 30 {fmt.Println(&quo…

剑指Offer题目笔记22(快速排序)

快速排序定义&#xff1a; ​ 快速排序的基本思想是分治法&#xff0c;排序过程如下&#xff1a;在输入数组中随机选取一个元素作为中间值&#xff08;pivot&#xff09;&#xff0c;然后对数组进行分区&#xff08;partition&#xff09;&#xff0c;使所有比中间值小的数据移…

DC电源模块的分类及特点介绍

BOSHIDA DC电源模块的分类及特点介绍 DC电源模块是一种将交流电转换为直流电的设备&#xff0c;广泛应用于各种电子设备中。根据其特点和功能&#xff0c;DC电源模块可以分为线性稳压模块和开关稳压模块两种。本文将详细介绍这两种DC电源模块的分类和特点&#xff0c;以便读者…

[C++初阶] 爱上C++ : 与C++的第一次约会

&#x1f525;个人主页&#xff1a;guoguoqiang &#x1f525;专栏&#xff1a;我与C的爱恋 本篇内容带大家浅浅的了解一下C中的命名空间。 在c中&#xff0c;名称&#xff08;name&#xff09;可以是符号常量、变量、函数、结构、枚举、类和对象等等。工程越大&#xff0c;名称…

京东云服务器价格_云主机价格查询系统_2024年京东云优惠活动

2024年京东云服务器优惠价格表&#xff0c;轻量云主机优惠价格5.8元1个月、轻量云主机2C2G3M价格50元一年、196元三年&#xff0c;2C4G5M轻量云主机165元一年&#xff0c;4核8G5M云主机880元一年&#xff0c;游戏联机服务器4C16G配置26元1个月、4C32G价格65元1个月、8核32G费用…

爬虫学习(爬取音乐)

import re import requestsurl "http://www.yy8844.cn/ting/numes/sussoc.shtml" response requests.get(url) response.encoding "gbk" # print(r.text) #第一步&#xff0c;访问网页获取MusicID p re.compile(r"MusicId(.*?);",re.S) prin…

VBA技术资料MF135:多值匹配查找

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…