(18)Linux 实现简易版shell

前言:做一个 "会创建,会终止,会等待,会程序替换" 的简易 shell 。

1、显示提示符和获取用户输入

shell 本质就是个死循环,我们不关心获取这些属性的接口,如果要实现 shell:

  • 1:显示提示符 →  #
  • 2:获取用户输入 → fgets
  • 3:将接收到的字符串拆开  →  把 "ls -a -l" 转换成  "ls"  "-a"  "-l" 
  • ……

我们先从简单的入手,先来实现前两步,显示提示符 获取用户输入

  1 #include <stdio.h>2 #include <string.h>3 #include <stdlib.h>4 #include <unistd.h>5 #include <sys/wait.h>6 #include <sys/types.h>7 8 #define NUM 10249 10 char cmd_line[NUM];   // 用来接收命令行内容11 12 int main(void)13 {14   //0.命令行解释器,一定是一个常驻内存的进程,不退出15     while (1) {16         /* 1:显示提示信息 */17         printf("[amx@hecs-0-1  myshell] # ");18         fflush(stdout);19 20         /* 2:获取用户输入,输入的是各种指令和选项 :"ls -a -l "*/21         memset (22             cmd_line,23             '\0',24             sizeof(cmd_line)25             );26        if( fgets(cmd_line, NUM, stdin)==NULL) {27           continue;28        }/* 从键盘获取,标准输入,stdin 29             获取到 C 风格的字符串,默认添加 '\0' */30         printf("%s\n", cmd_line);                                                                                                                                  31     }32 }

我们利用 fgets 函数从键盘上获取,标准输入 stdin,获取到 C 风格的字符串,

注意默认会添加 \0 ,我们先把获取到的结果 cmd_line 打印出来看看:

因为 cmd_line 里有一个 \n——我们输入完指令后会按下回车,printf里面又有一个回车  。我们把它替换成 \0 即可: 

cmd_line[strlen(cmd_line) - 1] = '\0';  //-1的目的是,最后一个字符的下标是长度减一,strlen不包括\n,\0这些 

2、将接收到的字符串拆开

下面我们需要 将接收到的字符串拆开,比如:把 "ls -a -l" 拆成  "ls"  "-a"  "-l"  

因为 exec 函数簇无论是列表传参还是数组传参,一定是要逐个传递的!

我们自己实现的话,就是把这些空格变成\0,让他们输出。

但是我们可以使用 strtok 函数,将一个字符串按照特定的分隔符打散,将子串依次返回:

char* strtok(char* str, const char* delim);

代码:

  1 #include <stdio.h>2 #include <string.h>3 #include <stdlib.h>4 #include <unistd.h>5 #include <sys/wait.h>6 #include <sys/types.h>7  8 #define NUM 10249 #define SIZE 3210 #define SEP " "//设置分隔符 空格11 12 //保存打散之后的命令行字符串13 char *g_argv[SIZE];14 char cmd_line[NUM];   // 用来接收完整的命令行内容15  16 int main(void)17 {18   //0.命令行解释器,一定是一个常驻内存的进程,不退出19     while (1) {20         /* 1:显示提示信息 */21         printf("[amx@hecs-0-1  myshell] # ");22         fflush(stdout);23                                                                                                                                      24         /* 2:获取用户输入,输入的是各种指令和选项 :"ls -a -l "*/                                                                     25         memset (                                                                                                                     26             cmd_line,                                                                                                                27             '\0',                                                                                                                    28             sizeof(cmd_line)                                                                                                         29             );                                                                                                                       30        if( fgets(cmd_line, NUM, stdin)==NULL) {                                                                                      31           continue;                                                                                                                  32        }/* 从键盘获取,标准输入,stdin                                                                                               33             获取到 C 风格的字符串,默认添加 '\0' */                                                                                  34         cmd_line[strlen(cmd_line) - 1] = '\0';  //-1的目的是,最后一个字符的下标是长度减一,strlen不包括\n,\0这些                    35       // printf("%s\n", cmd_line);                                                                                                   36       //3.命令行字符串解析:"ls -a -l "->"ls" "-a" "-l"                                                                              37       g_argv[0]= strtok(cmd_line, SEP);//第一次调用,要传入原始字符串  38       int index=1;39     while(g_argv[index++]=strtok(NULL,SEP));//第二次调用,如果还要解析原始字符串,传NULL40        //for debug 41          for(index=0;g_argv[index];index++){             42           printf("g_argv[%d]:%s\n",index,g_argv[index]);                                                                           43         }                                                                                                                                           44     }                                                                                                                                                            45 }                                                                                                                                                         
~      

运行结果:

3、创建进程 & 程序替换 

下面我们实现 创建进程,执行它。 

 1 #include <stdio.h>2 #include <string.h>3 #include <stdlib.h>4 #include <unistd.h>5 #include <sys/wait.h>6 #include <sys/types.h>7  8 #define NUM 10249 #define SIZE 3210 #define SEP " "//设置分隔符 空格11 12 //保存打散之后的命令行字符串13 char *g_argv[SIZE];14 char cmd_line[NUM];   // 用来接收完整的命令行内容15  16 int main(void)17 {18   //0.命令行解释器,一定是一个常驻内存的进程,不退出19     while (1) {20         /* 1:显示提示信息 */21         printf("[amx@hecs-0-1  myshell] # ");22         fflush(stdout);23  24         /* 2:获取用户输入,输入的是各种指令和选项 :"ls -a -l "*/25         memset (26             cmd_line, 27             '\0', 28             sizeof(cmd_line) 29             );                                                                                                                                                   30        if( fgets(cmd_line, NUM, stdin)==NULL) {31           continue;                                     32        }/* 从键盘获取,标准输入,stdin 33             获取到 C 风格的字符串,默认添加 '\0' */34         cmd_line[strlen(cmd_line) - 1] = '\0';  //-1的目的是,最后一个字符的下标是长度减一,strlen不包括\n,\0这些35       // printf("%s\n", cmd_line);36       //3.命令行字符串解析:"ls -a -l "->"ls" "-a" "-l"37       g_argv[0]= strtok(cmd_line, SEP);//第一次调用,要传入原始字符串38       int index=1;
W> 39       while(g_argv[index++]=strtok(NULL,SEP));                                                                                                                   40         //for debug41        // for(index=0;g_argv[index];index++){42          // printf("g_argv[%d]:%s\n",index,g_argv[index]);43        // }44       // 4.TODO45       // 5.fork()46       pid_t id=fork();47         if(id==0){//子进程48             printf("下面功能让子进程执行的\n");49             execvp(g_argv[0],g_argv);//ls -a -l -i50             exit(1);51         }52         else{//父进程53           int status=0;                                                                                                                                          54           pid_t ret=waitpid(id,&status,0);                        55           if(ret>0) printf("exit code: %d\n",WEXITSTATUS(status));56          57         }58      59     }60 }

完美运行,你还可以输入vim进行编写,基本命令都可以!!!!!!!

但是但是但是:

当我推出到上一级时,他的目录并没有发生什么变化,为什么?

当你在cd的时候,无论什么命令都会执行exec,而执行exec命令只会影响当前的子进程的路径变化,而不是父进程路径变化,运行完子进程,子进程就退出了,所以我们还需要再做一些工作------如果是像cd这样的命令,我们不能创建子进程。

像这样,让父亲在自己去执行的命令我们叫做内置命令\内建命令-----本质就是shell中的函数调用。

4、内建命令:实现路径切换

代码实现:

运行结果:

在上层你看到的是个命令,但是在 shell 内部本质上是由父 shell 自己实现、调用的一个函数(并没有创建子进程),这种就是对应上上层的 内建命令。

内建命令表现是用用户层面的一条命令,本质就是 Shell 内部的一个函数,由父 Shell 自己执行,而不创建子进程。

还可以加颜色:

我们which一下可以发现颜色配色。

运行结果:

全部搞定!!!!!!!!

//除了 ll这个命令

我们看,因为ll本身就是ls的别名,如果想让它成立,我们还需要做一些判断:

至此,圆满结束!!!

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

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

相关文章

015:JS之正则表达式,web乱码和路径问题总结,MVC架构模式

一 JS的正则表达式 1 正则表达式简介 正则表达式是描述字符模式的对象。正则表达式用于对字符串模式匹配及检索替换&#xff0c;是对字符串执行模式匹配的强大工具。 语法 var pattnew RegExp(pattern,modifiers);//正则的格式模版&#xff0c;修饰符或者更简单的方式:var pa…

权威测评首家通过!亚信安慧AntDB通过中国信通院数据库迁移工具专项测试

近日&#xff0c;亚信安慧数据库数据同步平台在中国信通院第17批“可信数据库”数据库迁移工具专项测试中&#xff0c;完全符合《数据库迁移工具能力要求》&#xff0c;成为首家通过标准测试的产品。这一成果标志着湖南亚信安慧科技有限公司&#xff08;简称“亚信安慧”&#…

工智能基础知识总结--为什么使用LSTM+CRF进行序列标注

为什么使用LSTM+CRF进行序列标注 直接使用LSTM进行序列标注时只考虑了输入序列的信息,即单词信息,没有考虑输出信息,即标签信息,这样无法对标签信息进行建模,所以在LSTM的基础上引入一个标签转移矩阵对标签间的转移关系进行建模。这一点和传统CRF很像,CRF中存在两类特征函…

Pandas透视表及应用

Pandas 透视表概述 数据透视表&#xff08;Pivot Table&#xff09;是一种交互式的表&#xff0c;可以进行某些计算&#xff0c;如求和与计数等。所进行的计算与数据跟数据透视表中的排列有关。 之所以称为数据透视表&#xff0c;是因为可以动态地改变它们的版面布置&#xf…

Git保姆级安装教程

Git保姆级安装教程 一、去哪下载二、安装2.1 具体安装步骤2.2 设置全局用户签名 一、去哪下载 1、官网&#xff08;有最新版本&#xff09;&#xff1a;https://git-for-windows.github.io/ 2、本人学习时安装的版本&#xff0c;链接&#xff1a;https://pan.baidu.com/s/1uAo…

机器学习-贝叶斯网络

贝叶斯分类器 贝叶斯网络是通过假设数据的先验分布&#xff0c;利用贝叶斯公式计算后验概率&#xff0c;将样本根据概率进行分类。 常用贝叶斯网络&#xff1a;1.朴素贝叶斯分类器&#xff1b;2.半朴素贝叶斯分类器&#xff1b;3.贝叶斯网&#xff1b;4.EM算法 朴素贝叶斯分…

Spark内核解析-数据存储5(六)

1、Spark的数据存储 Spark计算速度远胜于Hadoop的原因之一就在于中间结果是缓存在内存而不是直接写入到disk&#xff0c;本文尝试分析Spark中存储子系统的构成&#xff0c;并以数据写入和数据读取为例&#xff0c;讲述清楚存储子系统中各部件的交互关系。 1.1存储子系统概览 …

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统 随着油气工业的发展&#xff0c;管道泄漏成为一个严峻的安全问题。本文介绍了一种基于LabVIEW的分布式光纤油气管道泄漏检测及预警系统的设计思路和组成结构。系统包括硬件和软件两部分&#xff0c;其中硬件部分详细阐述了分…

Android.bp 常用模块类型

1 jar 用于编译 Java 代码并生成 .jar 文件。一般情况下&#xff0c;Android.bp 文件中会定义多个 jar 模块&#xff0c;每个 jar 模块对应着一个 Java 库 java_library {name: "mylibrary",srcs: ["src/**/*.java"],manifest: "AndroidManifest.xm…

安卓之数据存储管理的使用场景以及技术优劣分析

一、文章摘要 在安卓应用开发中&#xff0c;数据存储管理是关键环节之一&#xff0c;涉及到用户数据的持久化、应用程序状态的保存以及离线内容的缓存等场景。本文将探讨安卓数据存储管理的使用场景、应用前景&#xff0c;以及各种技术的优劣分析&#xff0c;同时附上相关代码示…

什么是软件测评?

什么是软件测评 软件测评是对软件进行评估和分析的过程&#xff0c;以确定软件的功能性、稳定性、易用性、可靠性、安全性、性能、兼容性和支持性等方面的表现。 软件测评通常包括以下几个方面&#xff1a; 功能性&#xff1a;评估软件是否具备预期的功能或符合产品描述。 稳…

阿里云被拉入黑洞模式怎么办?该怎么换ip-速盾网络

被拉入黑洞模式&#xff08;BGP黑洞路由&#xff09;意味着所有进入目标IP的流量都会被丢弃&#xff0c;从而导致目标IP对外完全不可访问。这种情况通常发生在面对大规模DDoS攻击时&#xff0c;为了防止攻击流量对其他网络造成影响。如果你使用的是阿里云服务并遭受到这种攻击&…

redis报错:Creating Server TCP listening socket 127.0.0.1:6379: bind: No error

Redis启动时报错&#xff1a; Creating Server TCP listening socket 127.0.0.1:6379: bind: No error 这个错误说明已经开启了redis&#xff0c;并且已经占用了端口6379&#xff0c;需要停止redis后再开启。 redis-cli.exeshutdownexitredis-server redis.windows.conf 参考…

java每日一题——输出星星塔(答案及编程思路)

前言&#xff1a; 打好基础&#xff0c;daydayup! 题目&#xff1a;请编写输出如下图的星星塔 编程思路&#xff1a;1&#xff0c;计算要输入几行&#xff1b;2&#xff0c;计算每行的⭐数量&#xff0c;及空格的数量&#xff1b;计算相应的关系&#xff1b; 如图&#xff1a;假…

Redis 有序集合(sorted set) 命令

目录 1.Redis Zrevrank 命令 - 返回有序集合中指定成员的排名&#xff0c;有序集成员按分数值递减(从大到小)排序简介语法可用版本: > 2.2.0返回值: 如果成员是有序集 key 的成员&#xff0c;返回成员的排名。 如果成员不是有序集 key 的成员&#xff0c;返回 nil 。 示例 2…

视频号掀起内容新风向,这几类账号为何爆红?

12月初&#xff0c;视频号就迎来了好消息&#xff0c;官方发布消息称&#xff0c;视频号作者加入互选的门槛由10000粉调整为5000粉&#xff0c;其他条件不变。此举旨在激励更多创作者积极投入视频内容创作&#xff0c;从而获得更多商业合作的机会和收益。 为帮助大家更好地洞察…

前端下载文件问题之如何获取报错信息

问题&#xff1a;点击下载后。接口会生成并返回文件流。在极端情况下接口数据返回异常&#xff0c;需要抛出错误信息&#xff0c;比如后端拼接错误情况、空文件情况。 难点&#xff1a;responseType设置为Blob后&#xff0c;返回内容为二进制文件流&#xff0c;从而无法获取错误…

如何不卸载docker情况下升级docker

原文&#xff1a;出处 idea连接不上docker或者拉取镜像报错&#xff1a;… missing signature key 1、rpm -qa | grep docker docker-1.13.1-53.git774336d.el7.centos.x86_64docker-client-1.13.1-53.git774336d.el7.centos.x86_64docker-common-1.13.1-53.git774336d.el7.cen…

Nginx学习之Nginx高性能的实现原理

Nginx学习之Nginx高性能的实现原理   Nginx 采用的是多进程&#xff08;单线程&#xff09; & 多路IO复用模型&#xff0c;使用了 I/O 多路复用技术的 Nginx&#xff0c;就成了”并发事件驱动“的服务器&#xff0c;同时使用sendfile等技术&#xff0c;最终实现了高性能。…

蓝牙串口协议(SPP)

目录 1. SPP概念 2. SPP角色介绍 3. SPP应用场景 1. SPP概念 SPP( Serial Port Profile ):蓝牙串口协议定义了使用蓝牙进行 RS232&#