【lesson17】minishell(shell的模拟实现)

文章目录

  • 模拟实现shell的思路
  • 具体实现
    • 一直循环(一)
    • 显示提示行符(二)
    • 获取用户输入的字符串(三)
    • 对字符串进行解析(四)
    • 创建子进程执行指令(5)
  • 细节问题解决
    • 问题一
    • 问题二
    • 问题三
    • 问题四
  • 代码
    • myshell.c
    • mytest.c

模拟实现shell的思路

1.首先shell一定是一直循环在运行的,不然我们无法一直读取指令。
Xshell的演示:
在这里插入图片描述
我们也看到Xshell也一直在等待,命令的输入,有命令输入则执行命令,无则一直循环等待。

2.其次显示提示符
在这里插入图片描述

我们会看到Xshell一直会显示一串括号内的提示符给我们

3.然后是获取用户输入的字符串
在这里插入图片描述
要执行OS的指令,首先我们要从键盘中读取出该指令,然后才能谈之后的事。

4.对字符串进行解析
我们从键盘中读取出来的指令是一串字符串,而我们要执行的指令是分开的一个一个的字符串,所以要对读取上来的字符串进行解析。

5.创建子进程执行指令。
为什么要创建子进程执行指令呢?
因为子进程执行指令的时候如果因为指令而引发崩溃等问题就不会影响到父进程,而父进程只要聚焦于读取数据,解析数据和派发任务即可。
在这里插入图片描述
我们可以看到真实的Xshell也是如此的。执行一个不存在的指令出错。但是不会影响Xshell的使用

具体实现

一直循环(一)

在这里插入图片描述
在这里插入图片描述

显示提示行符(二)

在这里插入图片描述
但是这里会有一个问题
在这里插入图片描述
我们会看到当我们运行自己的shell的时候会发现光标是在下一行的,而不是跟在提示行符后面的,我们看一下正常Xshell。
在这里插入图片描述
我们可以看到正常Xshell光标是跟在,提示行符后面的。
那么是因为什么呢?
其实很早之前在进度条代码的实现就讲过了,是==\n的问题==,具体细节这里就不过多介绍。

而我们只要把\n去掉,用系统的fflush函数就可以了。
在这里插入图片描述
在这里插入图片描述

获取用户输入的字符串(三)

1.首先我们要定义一个全局数据,用来保存获取的数据。
在这里插入图片描述
加粗样式2.其次是对cmd_line数组初始化
在这里插入图片描述
3.用fgets函数从stdin(标准输入)中读取数据。因为shell是也一直运行的,命令会一直被输入,所以fgets函数也要一直读取数据。
在这里插入图片描述

对字符串进行解析(四)

因为读取读取上来的是一组字符串,所以要对该字符串进行解析。
1.首先要创建命令数组,保存解析后的命令。
在这里插入图片描述
2.开始解析字符串
我们可以用strtok函数来解析字符串
因为我们输入命令的时候,使用空格隔开的,所以解析命令的时候,也要按照空额解析出来。
先定义空格
在这里插入图片描述
开始解析
在这里插入图片描述
strtok函数,第一次解析某个字符串要传入该字符串,接下来如果还是解析该字符串可以不用传,直接穿个NULL即可。

strtok返回值问题:
strtok解析字符串是一个一个解析的,解析完返回解析的字符子串。
演示:
在这里插入图片描述

创建子进程执行指令(5)

子进程执行指令
在这里插入图片描述
父进程等待子进程执行指令的结果。
在这里插入图片描述

上面子进程用进程替换执行指令,这里如果有问题的话,大家请移步进行替换的博客。

到这一步基本已经可以了。但是还有一些细节性的问题需要解决。

细节问题解决

问题一

在这里插入图片描述
我们在运行ls的时候会发现,系统的ls会带颜色,而我们自己代码执行的ls不会带颜色。
这是因为系统的ls是别名
在这里插入图片描述
所以我们碰到ls指令要特别处理,其实多传个颜色即可。
在这里插入图片描述
在这里插入图片描述

问题二

在这里插入图片描述
从图中我们会发现,我们自己的ll指令无法执行,因为系统中ll指令也是别名
在这里插入图片描述
而我们也特殊处理。
在这里插入图片描述
在这里插入图片描述

问题三

在这里插入图片描述
我们从图中可以看到cd不会改变路径的,但是正常的shell cd指令是要改变路径的这里为什么没有改变呢?
因为是子进程在执行命令,而父进程只分析命令等,所以子进程的路径发生了改变,但是父进程的路径并没有发生改变。

所以cd也要特殊处理。
在这里插入图片描述
在这里插入图片描述

问题四

首先我们建一个程序查找自己用我们的程序输入的环境变量。
在这里插入图片描述
在这里插入图片描述
我们会发现shell不允许我们自己的程序使用export。
所以对export指令我们也要单独处理。
在这里插入图片描述
但是这里有个非常隐蔽的问题。
在这里插入图片描述
我们会发现我们处理的第一步,就是给要输入的环境变量重新放一个地方。

因为我们自己的程序,在export结束后,就会清空g_argv数组的数据,而export不是直接把数据放进环境变量中的,是将指向该数据的指针放进环境变量中。所以当我们要查找该环境变量的时候就会发现什么都没有。
演示:
在这里插入图片描述

所以我们要创建一个数组将其保存起来。

但是这里也没有彻底解决这个问题,因为当再次export的时候,旧的export数据就有被覆盖,从而找不到。
演示:
在这里插入图片描述

代码

myshell.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>#define NUM 1024
#define SIZE 32
#define SEP " "char cmd_line[NUM];//array for saving command line
char* g_argv[SIZE];//the array are used to store paresed commands
char g_myval[64];
int main()
{//1.命令行解释器,一定是一个常驻内存的进程,不退出while(1){//2.显示提示行printf("[xiaolin@localhost myshell]#");fflush(stdout);memset(cmd_line,'\0',sizeof cmd_line);//3.获取用户输入的字符串if(fgets(cmd_line,sizeof cmd_line,stdin) == NULL){//if cmd_line empty contiue get commandcontinue;}                                                                                                                                                                   cmd_line[strlen(cmd_line)-1] = '\0';//4.对字符串进行解析int index = 0;g_argv[index++] = strtok(cmd_line,SEP);//Firest parse cmd_line while(1)//Second parse cmd_line don't pass cmdline;{g_argv[index] = strtok(NULL,SEP);if(g_argv[index] == NULL) break;index++;}if(strcmp(g_argv[0],"ls") == 0){g_argv[index++] = (char*)"--color=auto";g_argv[index] = NULL;} if(strcmp(g_argv[0],"ll") == 0){g_argv[0] = (char*)"ls";g_argv[1] = (char*)"-l";g_argv[2] = (char*)"--color=auto";g_argv[3] = NULL;}// processing of built-in commandsif(strcmp(g_argv[0],"cd") == 0){//chdir() changes the current working directory of the calling process to the directory specified in path.if(g_argv[1] != NULL) chdir(g_argv[1]);continue;}if(strcmp(g_argv[0],"export") == 0 && g_argv[1] != NULL){//There is a very hidden issue here//Ptuenv passes an environment variable as a pointer to it //And g_ Argv [1] will be cleared on the next command_line read//In this way, the environment variable pointer points to a place with empty data, and this pointer is also a null pointer//int res = putenv(g_argv[1]);//if(res == 0) printf("export success\n");                                                                                                                        //else printf("export fail\n");//solve the problemstrcpy(g_myval,g_argv[1]);int res = putenv(g_myval);if(res == 0) printf("export success\n");else printf("expor fail\n");continue;}/*//test if the g_argv array id correctfor(index = 0; g_argv[index]; index++){printf("g_argv[%d]:%s\n",index,g_argv[index]);}*///5.create subprocess execute commandpid_t id = fork();if(id == 0){//subprocess//printf("parent process create subprocess success\n");printf("subprocess starts running\n");execvp(g_argv[0],g_argv);printf("subprocess replace fail\n");}else if(id > 0){//parent processint status = 0;                                                                                                                                                   pid_t res = waitpid(-1,&status,0);//blocking waitingif(res == -1){printf("parent process wait subprocess fail\n");}else if(res > 0){printf("parent process wait subprocess success exit_code:%d\n",WEXITSTATUS(status));}else {printf("unkown error\n");}}else {//failprintf("parent procrss create subprocess fail\n");}}return 0;
}

mytest.c

#include <stdio.h>    
#include <stdlib.h>    int main()    
{    const char* s = "MYVAL";                                                                                                                                              char* res = getenv(s);    printf("%s=%s\n",s,res);    return 0;    
}

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

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

相关文章

Redis第2讲——Java三种客户端(Jedis、Lettuce和Redisson)

上篇文章介绍了Redis的9种数据类型和常命令、7种数据结构和9种编码方式。但是如果想要把它应用到项目中&#xff0c;我们还需要一个redis的客户端。redis的Java客户端种类还是很多的&#xff0c;其中使用最广泛的有三种——Jedis、lettuce和redisson&#xff0c;下面我们一起来…

web前端游戏项目-雷霆战机飞机大战游戏【附源码】

文章目录 一&#xff1a;雷霆战机HTML源码&#xff1a;JS文件&#xff1a;&#xff08;1&#xff09;function.js&#xff08;2&#xff09;impact.js&#xff08;3&#xff09;move.1.1.js&#xff08;4&#xff09;script.js 二&#xff1a;飞机大战HTML源码&#xff1a;CSS源…

性能压力测试--确保企业数字化业务稳健运行

随着企业的数字化转型和依赖云计算的普及&#xff0c;软件系统的性能已经成为企业成功运营的关键因素之一。性能压力测试作为确保系统在各种条件下都能高效运行的关键步骤&#xff0c;对企业的重要性不可忽视。以下是性能压力测试对企业的几个重要方面的影响和作用&#xff1a;…

最新AI创作系统ChatGPT系统源码+DALL-E3文生图+AI绘画+GPT语音对话功能

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

ubuntu 20.04安装一系列软件

1&#xff09;安装下载的包的指令&#xff1a; sudo dpkg -i xxx.deb 2&#xff09;通用指令&#xff1a; sudo apt-get install xxxx 3&#xff09;更新和升级软件包&#xff08;遇到问题先尝试这个指令&#xff09;&#xff1a; sudo apt-get update sudo apt-get install…

Java_集合进阶(Collection和List系列)

一、集合概述和分类 1.1 集合的分类 已经学习过了ArrayList集合&#xff0c;但是除了ArrayList集合&#xff0c;Java还提供了很多种其他的集合&#xff0c;如下图所示&#xff1a; 我想你的第一感觉是这些集合好多呀&#xff01;但是&#xff0c;我们学习时会对这些集合进行…

基于alibaba druid的血缘解析工具

基于alibaba druid的血缘解析 1、前言 仅仅对mysql数据库的select查询语句进行了血缘解析&#xff0c;该血缘解析包含了原始表字段、临时表字段和目标表字段的关联关系。 2、涉及到技术 主要使用了druid的如下接口对语法树进行解析&#xff1a; &#xff08;1&#xff09;…

JavaWeb笔记之前端开发CSS

一 、引言 1.1 CSS概念 层叠样式表(英文全称&#xff1a;Cascading Style Sheets)是一种用来表现HTML&#xff08;标准通用标记语言的一个应用&#xff09;或XML&#xff08;标准通用标记语言的一个子集&#xff09;等文件样式的计算机语言。CSS不仅可以静态地修饰网页&…

美颜技术详解:深入了解视频美颜SDK的工作机制

本文将深入探讨视频美颜SDK的工作机制&#xff0c;揭示其背后的科技奥秘和算法原理。 1.引言 视频美颜SDK作为一种集成到应用程序中的技术工具&#xff0c;通过先进的算法和图像处理技术&#xff0c;为用户提供令人印象深刻的实时美颜效果。 2.视频美颜SDK的基本工作原理 首…

SVN小白常见操作流程

SVN小白常见操作流程 一、什么是Subversion&#xff1f;二、TortoiseSVN客户端安装教程三、SVN 操作3.1 SVN Ckeckout(检出)3.2 Add(新增文件)3.3 SVN Commit(提交)3.4 SVN Update(更新操作)3.5SVN Delete(删除操作)3.6 SVN Revert to a revision(版本回溯)3.7 不同版本内容之间…

Jenkins 执行远程脚本的插件—SSH2 Easy

SSH2 Easy 是什么&#xff1f; SSH2 Easy 是一个 Jenkins 插件&#xff0c;它用于在 Jenkins 构建过程中通过 SSH2 协议与远程服务器进行交互。通过该插件&#xff0c;用户可以在 Jenkins 的构建过程中执行远程命令、上传或下载文件、管理远程服务器等操作。 以下是 SSH2 Eas…

vue3使用mock模拟后端接口

安装mock axios yarn add mock yarn add axios 新建在src/mockdata/automenu.js 模拟后端的json数据格式 import Mock from mockjs Mock.mock(/menu,get,{status: 200,menuList: [{id : 1,iconCls: "fa fa-window",name: 系统管理,url: /},{id: 2,icon: icon-j…

YOLOv8改进 | 主干篇 | 利用MobileNetV1替换Backbone(轻量化网络结构)

一、本文介绍 本文给大家带来的改进机制是MobileNetV1&#xff0c;其是专为移动和嵌入式视觉应用设计的轻量化网络结构。这些模型基于简化的架构&#xff0c;并利用深度可分离卷积构建轻量级深度神经网络&#xff0c;其引入了两个简单的全局超参数&#xff0c;用于在延迟和准确…

【C语言】指针详解(一)

目录 1.内存和地址 1.1内存 1.2如何理解编址 2.指针变量和地址 2.1取地址操作符&#xff08;&&#xff09; 2.2指针变量和解引用操作符&#xff08;*&#xff09; 2.2.1指针变量 2.2.2拆解指针类型 2.2.3解引用操作符 2.3指针变量大小 1.内存和地址 1.1内存 在讲内…

《数据分析-JiMuReport》积木报表详细入门教程

积木报表详细入门教程 一、JimuReport部署入门介绍 积木报表可以通过源码部署、SpringBoot集成、Docker部署以及各种成熟框架部署&#xff0c;具体可查看积木官方文档 当前采用源码部署&#xff0c;首先下载Jimureport-example-1.5.6 1 jimureport-example目录查看 使用ID…

自动气象监测站助力生活生产

随着科技的发展&#xff0c;我们的生活和生产方式正在发生着日新月异的变化。其中&#xff0c;WX-CQ12 自动气象监测站作为一项气象监测设备&#xff0c;正在发挥着越来越重要的作用。它不仅为我们提供了更加准确、实时的天气信息&#xff0c;还为农业、交通、旅游等领域提供了…

python flask+vue实现前后端图片上传

python flaskvue实现前后端图片上传 vue代码如下&#xff1a; <template><div><input type"file" change"handleFileChange"/><button click"uploadFile">上传</button><br><img :src"imageUrl&…

uniapp如何原生app-云打包

首先第一步&#xff0c;需要大家在HBuilder X中找到一个项目&#xff0c;然后呢在找到上面的发行选项 发行->原生App-云打包 选择完该选中的直接大包就ok。 大包完毕后呢&#xff0c;会出现一个apk包&#xff0c;这是后将这个包拖动发给随便一个人就行了。 然后接收到的那…

2-高可用-负载均衡、反向代理

负载均衡、反向代理 upstream server即上游服务器&#xff0c;指Nginx负载均衡到的处理业务的服务器&#xff0c;也可以称之为real server,即真实处理业务的服务器。 对于负载均衡我们要关心的几个方面如下&#xff1a; 上游服务器配置&#xff1a;使用upstream server配置上…

摄像头画面作为电脑桌面背景

1. 创建文件main.pyw&#xff0c;文件内容 import base64 import io import os import threading import tkinter as tkimport cv2 import pystray import win32api import win32con import win32gui from PIL import Image, ImageTk from pystray import MenuItem, Menuclass…