【bash 的简单模拟实现】

前言

打怪升级:第60天
在这里插入图片描述

bash模拟

初步代码

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

#define MARK " "
#define MAXSIZE 1024
#define SIZE 50

void Split(char*str, char* command[])  //  对指令进行切分 -- 函数  strtok(目标字符串, 切分条件字符串)
{
  int i=0;
  command[i] = strtok(str, MARK);// 指令名
  //while(command[i++])
  //{
  //  command[i] = strtok(NULL, MARK);
  //}
  while((command[++i] = strtok(NULL, MARK)));  //  化简一步

}

void Show(char* s[]) //  对切分结果打印验证
{
  for(int i=0; s[i]; ++i)
    printf("%s\n", s[i]);
}

int main()
{
while(1)
{
  printf("[陈好人@VM-8-3-centos 落魄山]# "); // 这里为了化简步骤,我们直接打印,不做特殊处理 -- 所以之后使用cd命令后提示语句不会改变
  fflush(stdout);

  char str[MAXSIZE] = "";
  fgets(str, MAXSIZE, stdin);// 获取指令集 -- 此处会将换行符一并读取
  str[strlen(str)-1] = '\0'; // 去掉 \n
  char* command[SIZE] = {0}; // 对 str进行拆分
  Split(str, command);
  //varShow(command);
  
  pid_t id = fork();
  assert(id >= 0);
  if(id == 0) // 使用子进程进行进程替换来调用对应指令
  { 
    execvp(*command, command);
  }
  else 
  {
    waitpid(id, NULL, 0);  // 防止父子交替执行时父进程执行太快。
  }
}
  return 0;
}

这里是引用


简单优化

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

#define MARK " "
#define MAXSIZE 1024
#define SIZE 50

void Split(char*str, char* command[])
{
  int i=0;
  command[i] = strtok(str, MARK);// 指令名
  //while(command[i++])
  //{
  //  command[i] = strtok(NULL, MARK);
  //}
  while((command[++i] = strtok(NULL, MARK)));

}

void Show(char* s[])
{
  for(int i=0; s[i]; ++i)
    printf("%s\n", s[i]);
}

extern char** environ;
extern int putenv(char*);
void PrintEnv()
{
  int i = 0;
  while(environ[i])
    printf("%d : %s\n", i, environ[i++]);
}

int main()
{
  while(1)
  {
    printf("[陈好人@VM-8-3-centos 落魄山]# ");
    fflush(stdout);
  
    char str[MAXSIZE] = "";
    fgets(str, MAXSIZE, stdin);// 获取指令集 -- 此处会将换行符一并读取
    str[strlen(str)-1] = '\0'; // 去掉 \n
    char* command[SIZE] = {0}; // 对 str进行拆分
    Split(str, command);
    //Show(command);
    
    if(strcmp(command[0], "cd") == 0)
    {
      //  cd指令需要父进程自己执行, 调用函数 chdir(char* path);
      if(command[1] != NULL)  chdir(command[1]);  
      continue; // 不需要往后执行了,直接返回
    }
   else if(strcmp(command[0], "env") == 0)
   {
      PrintEnv();
      continue;
   }   
 /*  else if(strcmp(command[0], "export") == 0)
   {
     if(command[1] != NULL)
     {
       int index = 0;
       while(environ[index]) ++index;             //  这样也加入不进去
       environ[index++] = (char* const)command[1];
       environ[index] = NULL;
     }
  //     putenv(command[1]); // 函数使用不了
    continue; 
   }*/
  else if(strcmp(command[0], "echo") == 0)
  {
    if(command[1][0] == '$') printf("%s\n", getenv(command[1] + 1));
    else printf("%s\n", command[1]);
    continue;
  }

    if(strcmp(command[0], "ls") == 0)
    {
      int i = 1;
      while(command[i]) ++i;
      command[i++] = (char*)"--color=auto"; // 颜色指令追加到最后 -- 注意,不能改变原字符串
      command[i] = NULL; //  最后仍然是 NULL
    }
    pid_t id = fork();
    assert(id >= 0);
    // (id); //  假装使用一次id,否则编译器会有警告
    if(id == 0) // 使用子进程进行进程替换来调用对应指令
    { 
      execvp(*command, command);
    }
    else 
    {
      waitpid(id, NULL, 0);
    }
  }
  return 0;
}

在这里插入图片描述

总结

以上只是我们对bash的十分十分简单的模拟,我们只有这区区一百行,而真正的bash解释器的代码量是以万为单位的,所以我们不需要特别纠结于哪一个哪一个功能没有实现,我们需要做的是理解所用到的语法与函数即可,希望对感兴趣的朋友提供了帮助。



  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值