linux入门---模拟实现命令行解释器

news2024/12/23 0:00:41

目录标题

  • 前提
  • 准备工作

前提

平时使用指令操作linux系统的时候可能会输入一些不好的指令,这些指令可能会对操作系统内核造成影响,所以就有了命令行解释器这个东西,它会过滤掉那些不好的指令从而让linux系统更加的安全,比如说我们输入一些不存在的命令,bash就会直接将这些指令过滤掉告诉我们该指令不存在,对于正确的指令bash就会帮我们执行这个指令的功能:
在这里插入图片描述
那么接下来我们就要模拟实现一个命令行解释,当然我们这里的模拟实现肯定是只是模拟个大概,具体的细节方面跟官方的肯定是不能比的,所以本篇文章的目的就是让大家大概的了解一下命令行解释器的工作原理。

准备工作

首先命令行解释器长这样:
在这里插入图片描述
它会告诉我们一些基本的信息比如说第一个xbb就表示当前登录用户的名字,@后面的VM-4-10-centos就表示当前使用的主机名,最后的~表明的就是当前的路径是~也就是xbb的家路径,我们将路径切换到其他地方去这个命令行解释器的内容就会发生改变:
在这里插入图片描述
我们在命令行解释器的后面输入对应的指令,执行完指令的内容之后命令行解释器就会自动在下一行打印出机器的相关信息,那么这里就是通过while循环来进行实现,在while循环的开始先使用printf语句打印出相关的信息,这里的信息可以通过环境变量来进行打印,在程序中获取环境变量的方法就是通过getenv函数,由于路径打印出来太长了我们自己实现的bash就不打印出路径的信息,比如说下面的代码:

#include<stdio.h>      
#include<stdlib.h>      
int main()      
{      
    while(1)    
    {    
        printf("[%s@%s 当前路径]",getenv("LOGNAME"),getenv("HOSTNAME"));
        fflush(stdout);    
        break;    
    }    
    return 0;    
} 

由于linux指令要在信息后面进行输入所以结尾不要加\n,while循环最后的break是为了方便我们进行测试到后面这个break是要删除的,那么上面的代码运行结果如下:
在这里插入图片描述
可以看到当前的能够正常的打印机器的信息,那么接下来我们就要输入具体的指令,首先得创建一个字符数组来记录输入的指令,由于指令分为具体的指令和指令对应的方法,所以还得创建一个字符指针数组用来记录指令字符串被切割生成的子串,这里可以通过fgets函数将键盘输入的数据存储到字符里面,这里大家要注意的一点就是得将字符数组的最后一个\n去掉不然会影响后序程序的运行
在这里插入图片描述
然后再使用strtok函数将字符数组里面的数据进行切割,平时我们在输入指令的时候是通过空格来作为分隔符,那么strtok的第二个参数我们就可以输入一个空格字符串
在这里插入图片描述
那么这一步的代码就如下:

  #include<stdio.h>
  #include<stdlib.h>                                                          
  #include<assert.h>
  #include<string.h>
  #define NUM 1024
  char linecommand[NUM];//用来记录指令
  char* myargv[NUM];//用来记录字串产生的子串
  int main()
  {
      while(1)
      {
          printf("[%s@%s 当前路径]",getenv("LOGNAME"),getenv("HOSTNAME"));
          fflush(stdout);
          char*s=fgets(linecommand,NUM,stdin);
          assert(s!=NULL);
          (void)s;
          linecommand[strlen(linecommand)-1]=0;
          myargv[0]=strtok(linecommand," ");
          int i=1;
          while(myargv[i++]=strtok(NULL," "));
          //当字符串分割完之后就会返回NULL然后就会顺便吧myargv数组的后一个元素
          //初始化为NULL来作为结尾
      }                                                                                                                                 
      return 0;                                                                                                        
  }   

分割完子串之后就可以使用fork创建子进程使用if语句将父子进程执行的代码分开,然后使用execvp函数对子进程实行进程替换让其执行指令的内容,execvp的函数介绍如下:
在这里插入图片描述
第一个参数表示执行的系统指名,第二个参数表明执行的方法,执行完execvp函数就可以使用exit函数来结束子进程,然后父进程就可以在外面实现进程等待,收集子进程执行程序的信息,既然这里要收集子进程的信息,那么外面就得再创建两个全局变量来记录子进程的信息,那么这里的代码就如下:

 #include<stdio.h>
  #include<stdlib.h>
  #include<unistd.h>
  #include<assert.h>
  #include<string.h>
  #include<sys/wait.h>
  #include<sys/types.h>
  #define NUM 1024
  char linecommand[NUM];//用来记录指令
  char* myargv[NUM];//用来记录字串产生的子串
  int lastcode =0;
  int lastsign=0;
  int main()
  {
      while(1)
      {
          printf("[%s@%s 当前路径]",getenv("LOGNAME"),getenv("HOSTNAME"));
          fflush(stdout);
          char*s=fgets(linecommand,NUM,stdin);
          assert(s!=NULL);
          (void)s;
          linecommand[strlen(linecommand)-1]=0;
          myargv[0]=strtok(linecommand," ");
          int i=1;
W>        while(myargv[i++]=strtok(NULL," "));
          //当字符串分割完之后就会返回NULL然后就会顺便吧myargv数组的后一个元素
          //初始化为NULL来作为结尾
          pid_t id=fork();
          if(id==0)                                                                                                                     
          {
              execvp(myargv[0],myargv);
              exit(1);
          }
          int status=0;
                    pid_t ret=waitpid(id,&status,0);
          assert(ret>0);
          (void) ret;
          lastcode=((status>>8)&0xFF);
          lastsign=(status&0x7F);
      }
      return 0;
  }


写道这里命令行解释器的外壳我们已经大致完成了,接下来我们就要测试一下上面这段代码的正确性:
在这里插入图片描述
这就是我们上面代码的运行结果,可以看到大致是实现成功的,但是这里有几个细节问题:第一个就是使用ls指令打印当前路径下的文件时没有显示颜色对吧,我们使用别人的ls指令是可以显示颜色的比如说下面的图片:
在这里插入图片描述
原因就是别人的ls指令会自动地添加一个"–color=auto"的选项,那么为了实现这个功能我们就可以在第一次切割子串的时候判断以下第一个字符是否是ls字符串,如果是的话我们就在myargv数组的后面手动的添加
"--color=auto"方法比如说下面的代码:

if(mygrv[0]!=NULL&&strcmp(myargv[0],"ls")==0)                                                                                                 
{    
   myargv[i]="--color=auto";    
   ++i;    
}    
while(myargv[i++]=strtok(NULL," "));    

再进行以下测试就可以看到我们实现的命令行解释器在执行ls指令的时候就会有颜色
在这里插入图片描述
上面的代码还有第二个问题就是cd指令无法修改路径,比如说下面的图片:
在这里插入图片描述
我们实现的命令行解释器好像无法指向cd指令,那这是为什么呢?原因很简单我们是通过创建子进程然后通过进程替换的方式来执行指令的文件,cd指令的作用是修改当前的工作路径,但是子进程和父进程是相互独立的,子进程的工作路径被修改了不会影响到父进程的工作路径,然后再执行pwd指令的时候创建的子进程又会从父进程中继承工作路径,所以打印的内容依然是不会发生变化的,所以要想解决这个问题就不能通过子进程来修改工作路径,而是得通过父进程来进行修改,所以当切割的第一个子串是cd的话我们就让父进程来执行这个指令的功能,系统提供了一个名为chdir的函数,这个函数可以修改当前进程的工作目录,所以通过这个函数我们就可以实现父进程的路径切换,我们来看看这个函数的参数:
在这里插入图片描述
这个函数只需要一个想要去的路径就可以,而cd指令后面跟着的一般都是想要去的路径,所以这里我们就可以加一个if语句进行判断,如果第二个子串的内容不为空的话我们就执行chdir函数,那这里的代码就如下:

if(myargv[0]!=NULL&&strcmp(myargv[0],"cd")==0)    
{    
     if(myargv[1]!=NULL)                                                                                                       
      {    
          chdir(myargv[1]);
          continue;    
      }                                                                                                                        
}                                                                                                                            

将这个代码添加上去cd指令就可以正常地运行了
在这里插入图片描述
最后一个问题就是echo指令,使用echo $?指令可以在屏幕上面打印出上一条指令执行地结果如何,但是我们我们我们这里实现地echo指令却只能打印出来一个$?,那么解决这个问题的方法也还是在父进程中单独进行处理,如果子串的第一个是echo,第二个不为空的话就单独进行处理:

if(myargv[0]!=NULL&&myargv[1]!=NULL&&strcmp(myargv[0],"echo")==0)
{                                                                    
                                                                                                                                        
}

处理的方式为:如果子串的第二个为$?的话就打印上一个子进程的退出信息也就是lastcode和lastsign的值,如果不为$?的话就直接打印第二个子串的值,那这里的代码就如下:

if(myargv[0]!=NULL&&myargv[1]!=NULL&&strcmp(myargv[0],"echo")==0)
{            
     if(strcmp(myargv[1],"$?")==0)
      {        
          printf("%d %d\n",lastcode,lastsign);
      }
     else
      {
           printf("%s\n",myargv[1]);
       }
   	continue;                                                                                                                  }

测试一下代码就可以看到正常的运行了
在这里插入图片描述
那么这就是我们实现的简单的命令行解释器,完整的代码如下:

#include<stdio.h>    
  #include<stdlib.h>    
  #include<unistd.h>    
  #include<assert.h>    
  #include<string.h>    
  #include<sys/wait.h>    
  #include<sys/types.h>    
  #define NUM 1024    
  char linecommand[NUM];//用来记录指令    
  char* myargv[NUM];//用来记录字串产生的子串    
  int lastcode =0;    
  int lastsign=0;    
  int main()    
  {    
      while(1)    
      {    
          printf("[%s@%s 当前路径]",getenv("LOGNAME"),getenv("HOSTNAME"));    
          fflush(stdout);    
          char*s=fgets(linecommand,NUM,stdin);    
          assert(s!=NULL);    
          (void)s;    
          linecommand[strlen(linecommand)-1]=0;    
          myargv[0]=strtok(linecommand," ");    
          int i=1;    
          if(myargv[0]!=NULL,strcmp(myargv[0],"ls")==0)    
          {    
             myargv[i]="--color=auto";    
              ++i;    
          }                                                                                                                             
          while(myargv[i++]=strtok(NULL," "));    
          //当字符串分割完之后就会返回NULL然后就会顺便吧myargv数组的后一个元素    
          //初始化为NULL来作为结尾    
          if(myargv[0]!=NULL&&strcmp(myargv[0],"cd")==0)    
          {        
              if(myargv[1]!=NULL)    
              {    
                  chdir(myargv[1]);    
                  continue;                                                                                                             
              }                                                                                                                         
          }
          if(myargv[0]!=NULL&&myargv[1]!=NULL&&strcmp(myargv[0],"echo")==0)
          {
             if(strcmp(myargv[1],"$?")==0)
             {
                printf("%d %d\n",lastcode,lastsign);
             }
             else
             {
                 printf("%s\n",myargv[1]);
             }
             continue;
          }
          pid_t id=fork();
          if(id==0)
          {
              execvp(myargv[0],myargv);
              exit(1);
          }
          int status=0;
          pid_t ret=waitpid(id,&status,0);
          assert(ret>0);
         (void) ret;
          lastcode=((status>>8)&0xFF);
          lastsign=(status&0x7F);
      }
      return 0;
  }

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

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

相关文章

初识linux之线程池与一些常见的锁

目录 一、线程池的概念 1. 线程池的概念 2. 线程池的应用场景 二、模拟实现一个线程池 1. 线程的简单封装 2. 锁的简单封装 3. 线程池的模拟实现 3.1 整体结构及头文件 3.2 构造函数 3.3 析构函数 3.4 线程启动 3.5 传入任务 3.6 整体结构 三、线程安全的单例模式…

向基于语义模型的操作集成的演变

在过去的许多年里&#xff0c;已经定义了许多架构方法&#xff0c;用于系统集成以及其信息和流程的表示。这些方法包括面向数据、面向消息、面向服务和面向信息的方法。需要探讨的问题是&#xff1a; 这些不同的方法有何不同和联系&#xff1f;从实时运营整合架构的角度来看&a…

MQTT协议理解

提示&#xff1a;记录mqtt服务搭建及访问教程 文章目录 前言一、MQTT是什么&#xff1f;二、使用步骤1.MQTT服务器搭建2.MQTT集成 总结 前言 一直想了解下mqtt&#xff0c;本人又懒&#xff0c;被动型学习&#xff0c;刚好项目需要&#xff0c;此篇记录下MQTT搭建过程及心得体…

小模型媲美2000倍体量大模型,谷歌提出新思路:蒸馏也能Step-by-Step

深度学习自然语言处理 原创作者&#xff1a;林哲乐 论文&#xff1a;Distilling Step-by-Step! Outperforming Larger Language Models with Less Training Data and Smaller Model Sizes地址&#xff1a;https://arxiv.org/pdf/2305.02301.pdf录取&#xff1a;ACL2023 进NLP群…

在JavaScript中4种创建枚举方式

本文译者为 360 奇舞团前端开发工程师 原文标题&#xff1a;4 Ways to Create an Enum in JavaScript原文作者&#xff1a;Dmitri Pavlutin原文地址&#xff1a;https://dmitripavlutin.com/javascript-enum/ 使用枚举&#xff08;enum&#xff09;可以方便地表示一个变量&…

一个通用的多相机视觉SLAM框架的设计和评估

文章&#xff1a;Design and Evaluation of a Generic Visual SLAM Framework for Multi-Camera Systems 作者&#xff1a;Pushyami Kaveti, Arvind Thamil Chelvan Hanumant Singh 编辑&#xff1a;点云PCL 来源&#xff1a;https://arxiv.org/pdf/2210.07315.pdf 代码&#x…

针对基于智能卡进行认证的活动目录的攻击

最近&#xff0c;我参与了一项攻击基于智能卡的活动目录的工作。实际上&#xff0c;你根本不需要使用物理智能卡来验证登录这个活动目录。证书的属性决定了它是否可以用于基于智能卡进行登录。因此&#xff0c;如果你能获得相应的私钥&#xff0c;那么就可以绕过智能卡的验证实…

借助ChatGPT自动生成PPT

借助ChatGPT自动生成PPT 首先让GPT生成一段markdown格式的PPT内容&#xff0c;尽量描述全面&#xff0c;以什么语言&#xff0c;什么格式&#xff0c;排版等等。 打开mindshow网址&#xff0c;点击import and create&#xff0c;选择以markdown方式创建&#xff0c;再次点击弹…

LayerNorm 在 Transformers 中对注意力的作用研究

LayerNorm 一直是 Transformer 架构的重要组成部分。如果问大多人为什么要 LayerNorm&#xff0c;一般的回答是&#xff1a;使用 LayerNorm 来归一化前向传播的激活和反向传播的梯度。 其实这只是部分正确&#xff1a;Brody、Alon 和 Yahav 的一篇题为“On the Expressivity Ro…

LabVIEWCompactRIO 开发指南20 应用程序接口

应用程序接口 STMAPI如图4.24所示。对于基本操作&#xff0c;它由一个读VI和一个写VI组成。它还具有两个补充VI&#xff0c;以帮助传输元数据&#xff0c;但它们的使用不是强制性的。每个主要的VI都是多态&#xff0c;这意味着可以将它们与不同的传输层一起使用。本文档讨论基…

WMS 窗口添加流程

WMS 系统窗口添加流程 文章目录 WMS 系统窗口添加流程一. addView二. addView代码分析2.1 应用端调用WindowManager的addView2.2 WindowManager的实现类是WindowManagerImpl2.3 WindowManagerGlobal2.4 setView2.4 addToDisplayAsUser&#xff08;Session.java&#xff09;2.5 …

韦东山Linux驱动入门实验班(2)hello驱动---驱动层与应用层通讯,以及自动产生设备节点

前言 &#xff08;1&#xff09;学习韦东山老师的Linux&#xff0c;因为他讲的很精简&#xff0c;以至于很多人听不懂。接下来我讲介绍韦东山老师的驱动实验班的第二个Hello程序。 &#xff08;2&#xff09;注意&#xff0c;请先学习完视频再来看这个教程&#xff01;本文仅供…

Oracle数据库实现limit功能

Oracle数据库不支持mysql中limit功能&#xff0c;但可以通过rownum来限制返回的结果集的行数&#xff0c;rownum并不是用户添加的字段&#xff0c;而是oracle系统自动添加的。 #1、使查询结果最多返回前100行&#xff1a; SELECT * FROM TESTSDK WHERE rownum<10; #2、查询结…

跟庄买股票得新技巧(2023.05.16)(绝密资料,只发一次)

昨天学了一个跟庄买卖股票的得新技能 统计昨天庄家异动的情况&#xff0c;按照行业分类&#xff08;板块&#xff09;板块对涨幅进行排序&#xff0c;涨幅排名分前三的是&#xff0c;龙头一&#xff0c;龙头二&#xff0c;龙头三买卖规则&#xff1a;看龙一&#xff0c;玩龙二…

如何使用Sentinel做流量控制?此文将附代码详细介绍Sentinel几种限流模式

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章将详细介绍Sentinel的两种限流模式&#xff0c;由于篇幅原因&#xff0c;后续文章将详细介绍Sentinel的其他三种。 如果文章有什么需要改进的地方还请大佬不吝赐教…

SCS【25】单细胞细胞间通信第一部分细胞通讯可视化(CellChat)

桓峰基因公众号推出单细胞生信分析教程并配有视频在线教程&#xff0c;目前整理出来的相关教程目录如下&#xff1a; Topic 6. 克隆进化之 Canopy Topic 7. 克隆进化之 Cardelino Topic 8. 克隆进化之 RobustClone SCS【1】今天开启单细胞之旅&#xff0c;述说单细胞测序的前世…

Servlet进阶API、监听器与过滤器

过滤器和监听器是Servlet规范里的两个高级特性&#xff0c; 过滤器的作用是通过对request、response 的修改实现特定的功能&#xff0c;例如请求数据字符编码、IP地址过滤、异常过滤、用户身份认证等。监听器的作用是用于监听Web程序中正在执行的程序&#xff0c; 根据发生的事…

罗德与施瓦茨Rohde Schwarz FSW8 2HZ-8GHZ信号分析仪FSW13收购

罗德与施瓦茨Rohde & Schwarz FSW8 2HZ-8GHZ信号分析仪 附加功能&#xff1a; 10 kHz 偏移&#xff08;1 GHz 载波&#xff09;时的低相位噪声为 –137 dBc (1 Hz) 用于 WCDMA ACLR 测量的 –88 dBc 动态范围&#xff08;带噪声消除&#xff09; 高达 2 GHz 的分析带宽 &…

Camtasia2023.0.1CS电脑录制屏幕动作工具新功能介绍

Camtasia Studio是一款专门录制屏幕动作的工具&#xff0c;它能在任何颜色模式下轻松地记录 屏幕动作&#xff0c;包括影像、音效、鼠标移动轨迹、解说声音等等&#xff0c;另外&#xff0c;它还具有即时播放和编 辑压缩的功能&#xff0c;可对视频片段进行剪接、添加转场效果。…

云原生背景下如何配置 JVM 内存

image.png 背景 前段时间业务研发反馈说是他的应用内存使用率很高&#xff0c;导致频繁的重启&#xff0c;让我排查下是怎么回事&#xff1b; 在这之前我也没怎么在意过这个问题&#xff0c;正好这次排查分析的过程做一个记录。 首先我查看了监控面板里的 Pod 监控&#xff1a;…