【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

代码实现

cpp 复制代码
#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解释命令的过程。

相关推荐
Ven%11 分钟前
centos查看硬盘资源使用情况命令大全
linux·运维·centos
萨格拉斯救世主1 小时前
戴尔R930服务器增加 Intel X710-DA2双万兆光口含模块
运维·服务器
Jtti1 小时前
Windows系统服务器怎么设置远程连接?详细步骤
运维·服务器·windows
TeYiToKu1 小时前
笔记整理—linux驱动开发部分(9)framebuffer驱动框架
linux·c语言·arm开发·驱动开发·笔记·嵌入式硬件·arm
dsywws1 小时前
Linux学习笔记之时间日期和查找和解压缩指令
linux·笔记·学习
yeyuningzi1 小时前
Debian 12环境里部署nginx步骤记录
linux·运维·服务器
上辈子杀猪这辈子学IT2 小时前
【Zookeeper集群搭建】安装zookeeper、zookeeper集群配置、zookeeper启动与关闭、zookeeper的shell命令操作
linux·hadoop·zookeeper·centos·debian
minihuabei2 小时前
linux centos 安装redis
linux·redis·centos
熊的猫2 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
EasyCVR2 小时前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频