(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;简称“亚信安慧”&#…

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…

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

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

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

LabVIEW开发分布式光纤油气管道泄漏检测及预警系统 随着油气工业的发展&#xff0c;管道泄漏成为一个严峻的安全问题。本文介绍了一种基于LabVIEW的分布式光纤油气管道泄漏检测及预警系统的设计思路和组成结构。系统包括硬件和软件两部分&#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;从而无法获取错误…

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

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

简单最短路径算法

前言 图的最短路径算法主要包括&#xff1a; 有向无权图的单源最短路径 宽度优先搜索算法&#xff08;bfs&#xff09; 有向非负权图的单源最短路径 迪杰斯特拉算法&#xff08;Dijkstra&#xff09; 有向有权图的单源最短路径 贝尔曼福特算法&#xff08;Bellman-Ford&#…

全志R128 SDK架构与目录结构

R128 S2 是全志提供的一款 M33(ARM)C906(RISCV-64)HIFI5(Xtensa) 三核异构 SoC&#xff0c;同时芯片内部 SIP 有 1M SRAM、8M LSPSRAM、8M HSPSRAM 以及 16M NORFLASH。本文档作为 R128 FreeRTOS SDK 开发指南&#xff0c;旨在帮助软件开发工程师、技术支持工程师快速上手&…

Kodi 开源多媒体播放器

Kodi (原名 XBMC) 是一款经典开源免费、跨平台且极其强大专业的多媒体影音播放器&#xff0c;包含专业的影音内容管理以及解码播放功能于一体&#xff0c;提供适合在手机/电视/投影/大屏幕上显示的全屏界面&#xff0c;无线手机遥控操作方式&#xff0c;以及功能相当丰富的插件…

Selenium-java元素等待三种方式

第二种方式需要写在创建driver时的代码下面 第三种则是对每个定位元素进行配置

Mybatis之多表查询

目录 一、简介 1、使用嵌套查询: 2、使用多个 SQL 语句&#xff1a; 3、使用关联查询&#xff1a; 4、使用自定义映射查询&#xff1a; 二、业务场景 三、示例 1、一对一查询 2、一对多查询 一、简介 MyBatis 是一个优秀的持久层框架&#xff0c;它提供了强大的支持来执…

/bin/bash: cannot execute binary file

容器内部无法执行二进制文件 原因是docker镜像的 入口点不能指向/bin/bash。移除ENTRYPOINT ["/bin/bash"]就足以使其正常工作。 如果是下载的镜像&#xff0c;不能修改ENTRYPOIN&#xff0c;可以使用dockerfile覆盖掉原来的ENTRYPOINT FROM ubuntu ENTRYPOINT [ …

常见的软件架构风格

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 以下是最常见的建筑风格&#xff1a; 整体式&#xff1a;将整个应用程序构建为一个单元&#xff0c;其中所有功能和组件都从一个位置进行管理和服务。整体架构的例…

使用“反向代理服务器”的优点是什么?

反向代理服务器是一种网络架构模式&#xff0c;通常位于客户端和实际服务器之间&#xff0c;用于处理客户端请求并转发到实际服务器。以下是使用反向代理服务器的优点&#xff1a; 1.安全性&#xff1a;反向代理服务器可以提供额外的安全层。通过在反向代理服务器上配置防火墙和…