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

news2024/11/24 14:56:46

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解释命令的过程。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flutter 开发学习笔记(0):环境配置

文章目录 前言开发需求环境配置运行出现问题我运行也是解决了很久的问题镜像源设置为清华的镜像源&#xff08;不知道有没有影响&#xff09;使用JDK17&#xff0c;测试过JDK21和JDK11都不行手动下载flutter 对应的gradle添加阿里云代理安卓编译下载 运行成功&#xff01; 前言…

Linux课程____Linux防火墙

一、包、过滤防火墙 包过滤内核&#xff1a;netfilter 规则管理工具&#xff1a;firewalld ,老版本linux: iptables工具 firewalld网络区域&#xff1a; 常用区域&#xff1a;trusted、home、public、external、block 二、格式 格式&#xff1a;firewall-cmd 【参数】 --per…

网络编程(三要素 ,IP、端口、协议)

二、网络三要素&#xff1a; 2.1 IP地址 接下来&#xff0c;我们详细介绍一下IP地址。IP&#xff08;Ineternet Protocol&#xff09;全称互联网协议地址&#xff0c;是分配给网络设备的唯一表示。IP地址分为&#xff1a;IPV4地址、IPV6地址 IPV4地址由32个比特位&#xff0…

【Qt】:坐标

坐标 一.常用快捷键二.使用帮助文档三.Qt坐标体系1.理论2.代码 一.常用快捷键 注释&#xff1a;ctrl / • 运⾏&#xff1a;ctrl R • 编译&#xff1a;ctrl B • 字体缩放&#xff1a;ctrl ⿏标滑轮 • 查找&#xff1a;ctrl F • 整⾏移动&#xff1a;ctrl shift ⬆/…

常见手撕项目C++

常见手撕项目C 设计模式单例模式饿汉模式懒汉模式 策略模式策略接口实现具体的策略&#xff08;虚函数重写&#xff09;定义上下文用户调用 设计模式 单例模式 单例模式是一种常用的软件设计模式&#xff0c;其目的是确保一个类只有一个实例&#xff0c;并提供一个全局访问点来…

HarmonyOS 应用开发之设置任务快照的图标和名称

设置任务快照的图标和名称是为了提高用户界面的可视化性和用户体验&#xff0c;以便更好地管理和跟踪应用程序中的任务和功能。通过为每个任务快照设置不同的图标和名称&#xff0c;可以更轻松地区分和识别每个任务的功能。 默认情况下任务快照的图标和名称采用的是 module.js…

bizcharts中LineChart时间戳使用moment转化出现Invalid Date

文章目录 一、前言1.1、问题1.2、解决 二、bizcharts三、moment.js四、在线源码五、最后 一、前言 1.1、问题 最近在使用bizcharts绘制折线图LineChart的时候&#xff0c;发现X轴的时间显示成了Invalid Date。如下图所示&#xff1a; 发现是后端返回了时间戳字符串"1572…

【HCIP学习】网络类型级数据链路层协议

思维导图在上面哦~ 一、网络类型的分类&#xff08;4种&#xff09; 出现原因&#xff1a;数据链路层使用的协议及规则不同&#xff0c;造成了不同的网络类型 1、多点接入网络&#xff08;MA&#xff09;------一条网段内上出现多个设备 BMA&#xff1a;广播型多点接入&…

增量式/绝对值式编码器,物体检测技术,位置距离检测技术

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 前言一、物体检测技术①、以“光”检测的方式②、以“涡电流”检测的方式③、以“接触”的检测方式④、以“超声波”的…

软考102-上午题-【信息安全】-杂题+小结

一、杂题 真题1&#xff1a; 真题2&#xff1a; 真题3&#xff1a; 真题4&#xff1a; 真题5&#xff1a; 真题6&#xff1a;

基于单片机宿舍防火防盗系统的设计

**单片机设计介绍&#xff0c;基于单片机宿舍防火防盗系统的设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机宿舍防火防盗系统的设计概要主要涉及单片机技术的应用&#xff0c;以实现对宿舍环境的防火和防盗功能的…

蓝桥杯刷题_day7_动态规划_路径问题

文章目录 DAY7下降路径最小和最小路径和地下城游戏 DAY7 下降路径最小和 【题目描述】 给你一个 n x n 的 方形 整数数组 matrix &#xff0c;请你找出并返回通过 matrix 的下降路径 的 最小和 。 下降路径 可以从第一行中的任何元素开始&#xff0c;并从每一行中选择一个元…

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

第一题《求和》【简单模拟】 【问题描述】 求1&#xff08;含&#xff09;至20230408&#xff08;含&#xff09;中每个数的和。 【答案提交】 这是一道结果填空的题&#xff0c;你只需要算出结果后提交即可。本题的结果为一个整数&#xff0c;在提交答案时只填写这个整数&…

brew install 命令详解

1. 前言 Homebrew 是一款 Mac OS 平台非常流行的软件包管理工具 通过一条简单的指令&#xff0c;就可以软件包管理&#xff0c;比如&#xff1a;安装、升级、更新等操作&#xff0c;这也是它这么流行的主要原因 2. 安装 安装软件命令格式 # 安装软件brew install <package…

css简单动画实现

html源码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>西安工程大学</title><link …

【解决方案】智慧路灯安全用电云平台解决方案

背景介绍 电力作为一种清洁能源&#xff0c;给人们带来了舒适、便捷的电气化生活。与此同时&#xff0c;由于使用不当&#xff0c;维护不及时等原因引发的漏电触电和电气火灾事故&#xff0c;也给人们的生命和财产带来了巨大的威胁和损失。 路灯漏电的主要原因分析&#xff1a…

【基于springboot分析Quartz(v2.3.2)的启动流程】

基于springboot分析Quartz&#xff08;v2.3.2&#xff09;的启动流程 最近公司的定时任务使用了Quartz框架&#xff0c;在开发中经常出现定任务不执行了的问题&#xff0c;但是我又找不到原因所在&#xff0c;可把我愁坏了。于是我决定看看Quartz框架是怎么调度任务的。&#x…

STM32 软件I2C方式读取AS5600磁编码器获取角度例程

STM32 软件I2C方式读取AS5600磁编码器获取角度例程 &#x1f516;本例程使用正点原子例程作为工程模板创建。 &#x1f4d8; 硬件电路部分 &#x1f33f;原理图部分&#xff1a; &#x1f33f;PCB布线和电路 &#x1f4d9;驱动代码部分 int main(void) {u16 i 0;u16 ra…

手动实现一个扩散模型DDPM

扩散模型是目前大部分AIGC生图模型的基座&#xff0c;其本质是用神经网络学习从高斯噪声逐步恢复图像的过程&#xff0c;本文用python代码从零开始构建了一个简单的扩散模型。 理论部分 DDPM(Denoising Diffusion Probabilistic Models) 是一种在生成对抗网络等技术的基础上发展…

【优选算法】双指针 -- 快乐数 和 盛最多水的容器

前言&#xff1a; &#x1f3af;个人博客&#xff1a;Dream_Chaser &#x1f388;刷题专栏&#xff1a;优选算法篇 &#x1f4da;本篇内容&#xff1a;03快乐数 和 04盛最多水的容器 目录 一、快乐数&#xff08;medium&#xff09; 1. 题⽬链接&#xff1a;202. 快乐数 2. …