Linux学习——模拟实现mybash小程序

目录

一,跟正宗的bash见个面

二,实现一个山寨的bash

1.提示符

2.输入命令与回显命令

 3.解析命令

4.执行命令

5.执行逻辑

三,全部代码


一,跟正宗的bash见个面

 在这篇文章中,我会写一个myshell小程序。这个小程序其实就是一个解释器。在我的机器上它长这样:

   

二,实现一个山寨的bash

1.提示符

在图:

中。这个提示符的信息可以分为四类:

1.用户名    2.主机名   3.当前地址   4.其他字符

在这个图片里:cq就是用户名  VM-8-9-centos就是主机名   mybash就是当前所在路径。

那我们该如何获取呢?两条路:1.其它字符直接打印  2.用户名等用环境变量获取。代码如下:

 #include<stdio.h>    
#include<stdlib.h>//getenv的头文件    
    
const char* Username()//获取用户名    
{    
  const char* user = getenv("USER");    
  if(user) return user;    
  return "none";    
}    
    
const char* Hostname()//获取主机名    
{    
  const char* host = getenv("HOSTNAME");    
  if(host) return host;    
  return "none";    
}    
    
const char* Pwd()  //获取当前地址  
{    
  const char* pwd = getenv("PWD");                                                                                                                                                         
  if(pwd) return pwd;    
  return "none";    
}    
    
    
int main()    
{    
  printf("[%s@%s %s]#\n",Username(),Hostname(),Pwd());    
  return 0;    
}    

效果:

可以看到我们当前的提示符显示是可以成功的。

2.输入命令与回显命令

    想到输入和显示命令时,我猜很多同学的脑子里第一个想到的便是scanf和printf。但是在这里我们是不能使用scanf的。因为我们在输入命令的时候一定会遇到输入空格的情况,如:ls -a -l命令等等。但是scanf在遇到空格的时候便会停下。所以我们不能使用scanf进行读取数据。所以我们采用gets或者fgets来读取数据。

   这两个函数介绍如下:

gets函数是将键盘上的输入读取到str缓冲区里存起来。

fgets的功能跟gets一样,这三个参数的意思如下:1.str表示存储读取到数据的地方

2.num 存储的最大数据量  。 3.stream表示从何读取(键盘读取则为stdin)。

写出代码如下:

 char buff[1024]\\一般将这个数组定义为全局的
 fgets(buff,sizeof(buff),stdin);                                                                                                                                                          
  buff[strlen(buff)-1] = '\0'; \\将回车符给吞掉  
  printf("%s\n",buff);  

封装函数如下:

void  getCommand()    
{    
  fgets(buff,sizeof(buff),stdin);    
  buff[strlen(buff)-1] = '\0';    
}  

效果:

 3.解析命令

  在这里解析命令的意思便是将一个长字符串以空格为分隔符分割成一个一个短的字符串。比如"ls -a -l"就应该分成"ls" "-a" "-l"。在这里我们要使用到一个字符串分割函数:

这个函数的参数:str表示要分割的字符串  delimiters表示分割符。并且要注意的是,当我的第一次分割成功以后,我后面的连续分割就可以将str用NULL表示。先在写出代码如下:

void splictCommand(char* in,char* out[]) \\注意这里的参数in是buff,out是char* argv。这两个参数都定义在全局   
  {    
    int argc = 0;    
    out[argc++]= strtok(in,SEP);    
  while(out[argc++]= strtok(NULL,SEP));   
 
  #ifdef Debug  \\用来测试  
    for(int i = 0;out[i];i++)    
      printf("%d:%s\n",i,out[i]);    
  #endif                                                                                                                                                                                   
  }   

效果:分割完成!!!

4.执行命令

在完成输入和解析命令以后我们就得来执行命令了。我们如何实现命令的执行呢?

1.创建子进程    2.使用程序替换。

在这里要了解的是,有一些命令是必须要让父进程来执行的。比如:cd export echo等。这些命令叫做内建命令。还有一些命令则不需要由父进程来来执行而是要交由子进程来执行。所以我们得创建子进程。 在执行命令的时候步骤如下:

1.先检查是否是内建命令:若是便执行并且返回一个1。若不是便返回0。

代码:

int dobuildin(char* argv[])
  {
    if(strcmp(argv[0],"cd")== 0)//cd是内建命令
    {
      char* path = NULL;
      if(argv[1] == NULL)  path =getHome();
      else  path = argv[1];
       cd(path);  
       return 1;
    }
    else if(strcmp(argv[0],"export") == 0)//export是内建命令
    {
       if(argv[1]== NULL) return 1; 
       strcpy(enval,argv[1]);
       putenv(enval);
       return 1;
    }
    else if(strcmp(argv[0],"echo")==0)//echo是内建命令
    {                                                                                                                                                                                      
      if(argv[1] == NULL)
      {
        printf("\n");
      }
      else{
       if(argv[1] == NULL) 
       {
         printf("\n");
         return 1;
       }
   if(*(argv[1])=='$'&&strlen(argv[1])>1)
       {
         
          char* val = argv[1]+1;
          if(strcmp(val,"?")==0)
          {
            printf("%d\n",lastcode);
            lastcode = 0;
          }
          else printf("%s\n",getenv(val));
       }
       else
       {
         printf("%s\n",argv[1]);
       }
       return 1;
    }
    }
  
  
    return 0;//不是内建命令便返回0
  
  }

然后才是执行其它命令:

void excute(char* argv[])
  {
     
    pid_t id = fork();//创建子进程
    if(id == 0)//子进程执行程序替换
    {
     execvp(argv[0],argv);
     exit(1);//执行完便可以退出
    }
    else 
    {
      int status = 0;
      pid_t  rid = waitpid(id,&status,0);//等待子进程
      if(rid>0){//等待成功
         lastcode = WEXITSTATUS(status);//获取最后一次的退出码
      }
    }
  }

执行逻辑:

 n = dobuildin(argv);//检查并执行内建命令
 if(n) continue;
 excute(argv);//子进程执行命令

这两个函数的执行顺序如上。如果内建命令执行成功在这一次便可以不再执行下面的普通命令的代码。如果不成功便可以执行下面的普通命令的代码。

5.执行逻辑

int main()
  {
   while(1){   
      char Usercommand[NUM];
      int n  =  getCommand(Usercommand,sizeof(Usercommand));//获取命令
      if(n<=0) continue;
      char* argv[SIZE];
      splictCommand(Usercommand,argv);//将命令打散放到数组中                                                                                                                               
      n = dobuildin(argv);//检查并执行内建命令
      if(n) continue;
      excute(argv);//子进程执行命令
   }
      return 0;
  }

三,全部代码

#include<stdio.h>
#include<stdlib.h>//getenv的头文件
#include<string.h>
#include<unistd.h>//fork的头文件
#include<sys/types.h>//要使用pid_t必须包含的头文件  
#include<wait.h>

#define Debug 1
char buff[1024];
char* argv[64];
char enval[1024];//用来存储全局的环境变量
char cwd[1024];
int lastcode = 0;

#define SEP " "
const char* Username()//获取用户名
{
  const char* user = getenv("USER");
  if(user) return user;
  return "none";
}

const char* Hostname()//获取主机名
{
  const char* host = getenv("HOSTNAME");
  if(host) return host;
  return "none";
}

const char* Pwd()
{
  const char* pwd = getenv("PWD");
  if(pwd) return pwd;
  return ".";
}

 char* getHome()
{
   char*  home = getenv("HOME");
  if(home) return home;
  return(char*) "none";
}

int  getCommand()
{

  printf("[%s@%s %s]#",Username(),Hostname(),Pwd());
  char* str = fgets(buff,sizeof(buff),stdin);
  buff[strlen(buff)-1] = '\0';
  if(str) return strlen(str)-1;
  return -1;
}

void splictCommand(char* in,char* out[])
{
  int argc = 0;
  out[argc++]= strtok(in,SEP);
  while(out[argc++]= strtok(NULL,SEP));
#ifdef Debug
  for(int i = 0;out[i];i++)
    printf("%d:%s\n",i,out[i]);
#endif
}

void cd( char* path)
{
  if(path == NULL)
  {
     path = getHome();
  }
   
  int i= chdir(path);
  printf("%d\n",i);
   char temp[1024];
   getcwd(temp,sizeof(temp));//获取pwd并放到临时变量temp中
   sprintf(cwd,"PWD=%s",temp);将pwd放到全局变量cwd中
   putenv(cwd);//用cwd替换掉PWD内的内容实现改变PWD的目的
}

int  dobuildin( char* argv[])
{
   if(strcmp(argv[0],"cd") == 0)
   {
     char* path = argv[1];      
      cd(path);
      return 1;
   }
   else if(strcmp(argv[0],"export")== 0)
   {
        
        char* val = argv[1];
        if(val == NULL) return 1;
        strcpy(enval,val);
        putenv(enval);
        return 1;
   }
   else if(strcmp(argv[0],"echo")== 0)
   {
     
      if(*argv[1]=='$'&&strlen(argv[1])>1)
      {
        
      char* val = argv[1]+1;//$?,$PATH
      if(strcmp(val,"?")==0)
        {
        printf("%d\n",lastcode);//显示最近一次错误码
        lastcode = 0;
        return 1;
        }    
        else {
           printf("%s\n",getenv(val));
        } 
     }
      else
      {
        printf("%s\n",argv[1]);
      }
      return 1;
   }

   return 0;
}

void excute(char* argv[])
{
  
    pid_t id =fork();
    if(id == 0)//子进程 
    {
      execvp(argv[0],argv);
      exit(1);
    }
  
    else//父进程
    {
      int status = 0;
      pid_t rid = waitpid(id,&status,0);//等待子进程
      if(rid>0){
        lastcode = WEXITSTATUS(status); //获取退出码
      } 

    } 
}

int main()
{
  while(1)
  {
    int n = getCommand();
    if(n<=0) continue;
    splictCommand(buff,argv);
     n =  dobuildin(argv);
    if(n) continue;
    excute(argv); 
    
  }
    return 0;
}

   

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

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

相关文章

Linux中top命令输出日志分析?

以下是对输出的各部分的解释&#xff1a; 09:54:34&#xff1a;系统当前时间。up 161 days, 2:08&#xff1a;系统已经运行了161天2小时8分钟。5 users&#xff1a;有5个用户登录系统。load average: 0.13, 0.08, 0.05&#xff1a;系统的1分钟、5分钟、15分钟的平均负载。负载…

数据结构与算法之美学习笔记:29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词?

目录 前言堆的应用一&#xff1a;优先级队列堆的应用二&#xff1a;利用堆求 Top K堆的应用三&#xff1a;利用堆求中位数解答开篇内容小结 前言 本节课程思维导图&#xff1a; 搜索引擎的热门搜索排行榜功能你用过吗&#xff1f;搜索引擎每天会接收大量的用户搜索请求&#x…

Opencv制作电子签名(涉及知识点:像素过滤,图片通用resize函数,像素大于某个阈值则赋值为其它的像素值)

import cv2def resize_by_ratio(image, widthNone, heightNone, intercv2.INTER_AREA):img_new_size None(h, w) image.shape[:2] # 获得高度和宽度if width is None and height is None: # 如果输入的宽度和高度都为空return image # 直接返回原图if width is None:h_ratio …

手机上的记事本怎么打开?安卓手机通用的记事本APP

有不少上班族发现&#xff0c;自己想要在电脑上随手记录一些工作文字内容&#xff0c;直接使用电脑上的记事本工具来编辑文字是比较便捷的。但是如果想要在手机上记录文字内容&#xff0c;就找不到手机上的记事本了。那么手机上的记事本怎么打开&#xff1f;安卓手机通用的记事…

MySQL:找回root密码

一、情景描述 我们在日常学习中&#xff0c;经常会忘记自己的虚拟机中MySQL的root密码。 这个时候&#xff0c;我们要想办法重置root密码&#xff0c;从而&#xff0c;解决root登陆问题。 二、解决办法 1、修改my.cnf配置文件并重启MySQL 通过修改配置文件&#xff0c;来跳…

Symbol()和迭代器生成器

目录 1、Symbol&#xff08;&#xff09; 2、迭代器生成器 执行流程 模拟生成器函数 for of 遍历迭代选择器 yield * Generator函数应用 1、Symbol&#xff08;&#xff09; Symbol表示独一无二的值 const s1 Symbol(a)const s2 Symbol(a)console.log(s1 s2) // fa…

Elasticsearch 如何处理 Aggs 顺序中的大写字母和小写字母?

Elasticsearch 排序允许你根据特定条件对搜索结果进行排序。 然而&#xff0c;在排序时处理区分大小写时&#xff0c;Elasticsearch 将大写和小写字母视为不同的字符&#xff0c;分别对它们进行排序。 这是因为 ASCII 表顺序是从大写 A 到小写 z。 默认情况下&#xff0c;Elas…

MySQL之性能分析和系统调优

MySQL之性能分析和系统调优 性能分析 查看执行计划 EXPLAIN EXPLAIN作为MySQL的性能分析神器&#xff0c;可以用来分析SQL执行计划&#xff0c;需要理解分析结果可以帮助我们优化SQL explain select … from … [where ...]TABLE 表名 查询的每一行记录都对于着一张表 id 该…

【数据结构】最短路径(Dijskra算法)

一.引例 计算机网络传输的问题&#xff1a; 怎样找到一种最经济的方式&#xff0c;从一台计算机向网上所有其他计算机发送一条消息。 抽象为&#xff1a; 给定带权有向图G&#xff08;V&#xff0c;E&#xff09;和源点v&#xff0c;求从v到G中其余各顶点的最短路径。 即&…

优化问题,详解静态优化

优化问题&#xff0c;尤其静态优化问题&#xff0c;在控制系统设计中随处可见&#xff0c;例如基于燃油经济性和驾驶体验的多目标优化的汽车发动机 MAP 标定&#xff0c;基于性能指标优化的飞行器结构设计参数优化&#xff0c;以实验数据与模型输出匹配为目标的电池 RC 等效电路…

[WP] ISCTF2023 Web 部分题解

圣杯战争!!! 反序列化伪协议读取 where_is_the_flag 环境变量根目录当前目录 绕进你的心里 利用正则最大回溯绕过 easy_website or select 用双写绕过 空格用/**/绕&#xff0c;报错注入 wafr codesystem(ca\t /f*) webinclude 扫描得到index.bak备份文件打开为加密的代码 写…

Linux Spug自动化运维平台本地部署与公网远程访问

文章目录 前言1. Docker安装Spug2 . 本地访问测试3. Linux 安装cpolar4. 配置Spug公网访问地址5. 公网远程访问Spug管理界面6. 固定Spug公网地址 前言 Spug 面向中小型企业设计的轻量级无 Agent 的自动化运维平台&#xff0c;整合了主机管理、主机批量执行、主机在线终端、文件…

SAP_ABAP_编程基础_数据集_创建并填充摘录数据集 / 处理摘录数据集

SAP ABAP 顾问&#xff08;开发工程师&#xff09;能力模型_Terry谈企业数字化的博客-CSDN博客文章浏览阅读494次。目标&#xff1a;基于对SAP abap 顾问能力模型的梳理&#xff0c;给一年左右经验的abaper 快速成长为三年经验提供超级燃料&#xff01;https://blog.csdn.net/j…

【U8+】用友U8修改凭证提示:外部凭证在总账系统中不能修改。

【问题描述】 用友U8中&#xff0c;在总账模块中修改凭证的时候&#xff0c;提示&#xff1a; 外部凭证在总账系统中不能修改。 【原因分析】 在软件中&#xff0c;使用其他模块的情况下&#xff0c; 其他模块生成的凭证都会传到总账模块中&#xff0c;进而这些由其他模块生成…

uniapp设置手机通知权限以及uniapp-push2.0推送

unipush2.0代码 export default function () {// 调用获取用户通知权限setPermissions()// 获取客户端唯一的推送标识&#xff0c;可用于测试uni.getPushClientId({success: (res) > {console.log(res.cid)},fail(err) {console.log(err)}})// 监听推送uni.onPushMessage(r…

windows ssh时出现Bad local forwarding specification的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

TA-Lib学习研究笔记——Cycle Indicators (七)

TA-Lib学习研究笔记——Cycle Indicators &#xff08;七&#xff09; Cycle Indicators 周期指标函数组有HT_DCPERIOD, HT_DCPHASE, HT_PHASOR, HT_SINE, HT_TRENDMODE 。 1.HT_DCPERIOD Hilbert Transform - Dominant Cycle Period 函数名&#xff1a;HT_DCPERIOD 名称&am…

Hertz 整合swagger

文章目录 Swagger安装使用用法项目demoSwagger注释用法通用API信息 swag命令行参数swagger路由配置 Swagger 安装 go get 安装可执行文件需要配合 GOPATH 模式工作。 go get github.com/swaggo/swag/cmd/swag 因为从 Go 1.17 开始&#xff0c;在 go mod 模式下通过 go get 下…

【Web】NISACTF 2022 个人复现

目录 ①easyssrf ②babyupload ③ level-up ④bingdundun~ 明天就新生赛了&#xff0c;练套题保持下手感吧 &#xff08;文章只选取了一部分&#xff09; ①easyssrf 输入/flag 输入file:///fl4g 访问/ha1x1ux1u.php ?filephp://filter/convert.base64-encode/resource/…

uni-app 微信小程序之自定义中间圆形tabbar

文章目录 1. 自定义tabbar效果2. pages新建tabbar页面3. tabbar 页面结构4. tabbar 页面完整代码 1. 自定义tabbar效果 2. pages新建tabbar页面 首先在 pages.json 文件中&#xff0c;新建一个 tabbar 页面 "pages": [ //pages数组中第一项表示应用启动页&#xff…