【Linux】编写一个简易的shell

思维导图

学习目标

       将简易的shell代码进行编写。

一、阐述shell的基本思路

       在进程程序替换中,我们可以将一个指令交给子进程,让子进程去完成这个指令。如果这个命令是一个内建命令,我们需要将这个命令交给bash进行处理。

       大致思路是:首先,我们先打印出来一行命令行,代表我们的主机名,名字和当前路径;之后捕获一行指令命令,将指令命令进行分割,存储在字符串指针数组中;然后,将这个字符串指针数组交给exec*函数进行程序替换。

二、输出一个命令行

2.1 思路和代码

           

       我们可以观察xshell中的这个命令行中的内容,我们可以仿效这个命令行中的内容打印出自己的xshell的命令行。现在我们应该思考从哪里获取这个内容呢??在环境变量中,我们可以发现有这些内容:

       我们可以使用getenv函数来分别获取USER、HOSTNAME、PWD的内容,之后使用snprintf函数将这个内容串联起来打印到一个字符串数组,以便将这个命名行打印出来。

const char* Getname()
{const char* name = getenv("USER");if(name == NULL) return "None";return name;
}const char* Gethostname()
{const char* hostname = getenv("HOSTNAME");if(hostname == NULL) return "None";return hostname;
}const char* Getpwd()
{const char* pwd = getenv("PWD");if(pwd == NULL) return "None";return pwd;
}void MakeCommendLine(char commend[], size_t size)
{const char* name = Getname();const char* hostname = Gethostname();const char* pwd = Getpwd();SkipPath(pwd);snprintf(commend, size, "[%s@%s %s]>" , name, hostname, strlen(pwd) == 1 ? "/" : pwd + 1);printf("%s", commend);fflush(stdout);
}

2.2 简要介绍一下snprintf函数

char *getenv(const char *name)

       函数的用途:该函数返回一个以 null 结尾的字符串,该字符串为被请求环境变量的值。如果该环境变量不存在,则返回 NULL。

2.3 简要介绍一下getenv函数

int snprintf(char *str, size_t size, const char *format, ...)
  1. 如果格式化后的字符串长度 < size,则将此字符串全部复制到str中,并给其后添加一个字符串结束符('\0');
  2. 如果格式化后的字符串长度 >= size,则只将其中的(size-1)个字符复制到str中,并给其后添加一个字符串结束符('\0'),返回值为欲写入的字符串长度。
  3. snprintf的返回值n,当调用失败时,n为负数,当调用成功时,n为格式化的字符串的总长度(不包括\0),当然这个字符串有可能被截断,因为buf的长度不够放下整个字符串。

三、获取用户命令字符串

       我们在输入指令命令时,会有空格,我们不能使用scanf函数,所以我们应该使用fgets函数,将指令命令进行接收。

int getecho(char* commend, size_t n)
{char* s = fgets(commend, n, stdin);if(s == NULL) return -1;commend[strlen(commend) - 1] = '\0';return strlen(commend);
}

简要介绍一下fgets函数

char *fgets(char *restrict str, int size, FILE *restrict stream)

函数的用途:fgets函数就是用来读取一行数据的,从第三个参数指定的流中读取最多第二个参数大小的字符到第一个参数指定的容器地址中。

函数的返回值:在正常情况下fgets()函数的返回值和它第一个参数相同。即读取到数据后存储的容器地址。但是如果读取出错或读取文件时文件为空,则返回一个空指针。

函数的注意事项:fgets()函数的眼里,换行符’\n’也是它要读取的一个普通字符而已。在读取键盘输入的时候会把最后输入的回车符也存进数组里面,即会把’\n’也存进数组里面,而又由于字符串本身会是以’\0’结尾的。所以在输入字符个数没有超过第二个参数指定大小之前,你输入n个字符按下回车输入,fgets()存储进第一个参数指定内存地址的是n+2个字节。最后面会多出一个’\n’和一个’\0’,而且’\n’是在’\0’的前面一个(\n\0)。其余部分请看大佬写的:fgets函数详解

四、切割命令字符串

       在获取到输入的指令字符串后,我们需要将指令进行切割。因为指令间隔是空格,我们可以使用strtok函数进行分割指令。

char* gargv[SIZE];void slashecho(char commend[], size_t n)
{gargv[0] = strtok(commend, SEP);int cnt = 1;while ((gargv[cnt++] = strtok(NULL, SEP))); // 故意写出赋值,
}

简要介绍一下strtok函数

char *strtok(char s[], const char *delim);

函数的用途:分解字符串为一组字符串。s为要分解的字符,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。

函数的返回值:从s开头开始的一个个被分割的串。当s中的字符查找到末尾时,返回NULL。如果查找不到delim中的字符时,返回当前strtok的字符串的指针。所有delim中包含的字符都会被滤掉,并将被滤掉的地方设为一处分割的节点。 

五、创建子进程进行进程替换

5.1 检查指令是否为内建命令

       比较草率,直接利用if语句进行逐一的判断,如果成功,则是内建命令;如果失败,则是普通命令。如果是内建命令,我们可以重新创建一个函数来单独的进行内建命令的执行。

int ChickBuliding()
{int yes = 0;const char* entercommend = gargv[0];if(strcmp(entercommend, "cd") == 0){yes = 1;Cd();}else if(strcmp(entercommend, "echo") == 0 && strcmp(gargv[1], "$?") == 0){yes = 1;printf("%d\n", lastcode);lastcode = 0;}return yes;
}

       比如,说cd命令,我们可以利用chdir函数改变当前工作目录,getcwd函数将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,参数size为buf的空间大小。在将获取到的路径写入cwd中,最后利用putenv函数将cwd写入环境变量中。

void Cd()
{const char* path = gargv[1];if(path == NULL) path = Home();chdir(path);// 刷新环境变量char temp[SIZE * 2];// 获取当前路径getcwd(temp, sizeof temp);// 将当前路径写入cwd中snprintf(cwd, sizeof cwd, "PWD=%s", temp);// 将cwd写入环境变量中putenv(cwd);
}

       还有一个echo $?命令,直接判断是否为这个命令,如果是这个命令,直接将lastcode返回,并将lastcode重新置为0。

5.2 指令是普通命令

       我们可以创建一个子进程,利用exec*函数进行程序进程替换。最后,让父进程进行等待,如果父进程等待成功,则检查退出码是否为0,如果不为0,将错误信息打印出来。

void executecommend()
{pid_t id = fork();if(id < 0) {Die();}else if(id == 0){execvp(gargv[0], gargv);exit(1);}else {int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){lastcode = WEXITSTATUS(status);if(lastcode != 0) printf("%s:%s:%d\n", gargv[0], strerror(lastcode), lastcode);}}
}

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

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

相关文章

自然资源-地质勘查工作的流程梳理

自然资源-地质勘查工作的流程梳理 地质勘查从广义上可理解为地质工作&#xff0c;地质队员就好像是国家宝藏的“寻宝人”&#xff0c;通过地质勘查&#xff0c;为国家找矿&#xff0c;以保障国家能源资源安全和服务国计民生&#xff0c;发挥着地质工作在国民经济建设中的基础性…

Spring框架学习笔记(一):Spring基本介绍(包含IOC容器底层结构)

1 官方资料 1.1 官网 https://spring.io/ 1.2 进入 Spring5 下拉 projects, 进入 Spring Framework 进入 Spring5 的 github 1.3 在maven项目中导入依赖 <dependencies><!--加入spring开发的基本包--><dependency><groupId>org.springframework<…

网络编程套接字和传输层tcp,udp协议

认识端口号 我们知道在网络数据传输的时候&#xff0c;在IP数据包头部有两个IP地址&#xff0c;分别叫做源IP地址和目的IP地址。IP地址是帮助我们在网络中确定最终发送的主机&#xff0c;但是实际上数据应该发送到主机上指定的进程上的&#xff0c;所以我们不仅要确定主机&…

看懂这4点,保证让你顺利申请小程序地理位置接口

小程序地理位置接口有什么功能&#xff1f; 通常情况下&#xff0c;我们在开发小程序时&#xff0c;可能会用到获取用户地理位置信息的功能。小程序开发者开放平台的新规定指出&#xff0c;如果没有申请开通微信小程序地理位置接口&#xff08;getLocation&#xff09;&#xf…

机器学习算法应用——K近邻分类器(KNN)

K近邻分类器&#xff08;KNN&#xff09;&#xff08;4-2&#xff09; K近邻分类器&#xff08;K-Nearest Neighbor&#xff0c;简称KNN&#xff09;是一种基本的机器学习分类算法。它的工作原理是&#xff1a;在特征空间中&#xff0c;如果一个样本在特征空间中的K个最相邻的样…

基于flowable没有规则的并发网关流程跳转记录分析

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://218.75.87.38:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; h…

Istio中的全局限流方案

Istio中的全局限流方案 在k8s网格&#xff08;istio&#xff09;环境中&#xff0c; 可以通过创建Envfoyfilter的方式来配置限流。 在istio官方文档中&#xff0c;提供了两种限流方式&#xff1a; 本地限流全局限流 本地限流的细节这里不再赘述, 主要讲解全局限流的配置方式…

Amesim基础篇-热仿真常用模型库-Air Conditioning-Pipes

前言 基于上文对空调库各个元件的介绍&#xff0c;本文进一步将其中的管路展开。 管路介绍 1 摩擦阻力管&#xff08;R&#xff09;&#xff1a; 具有阻力特性的管路&#xff0c;通过管长以及管截面计算阻力。 2 可调节阻力管&#xff08;R&#xff09;&#xff1a; 只具有…

Vision Mamba:高效视觉表示学习双向状态空间模型,超越Vision Transformer!

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; Vision Mamba: Efficient Visual Representation Learning with Bidirectional State Space Model 引言&#xff1a;探索视觉领域的新方向 在计算机视觉领域&…

探索 Joomla! CMS:打造个性化网站的利器

上周我们的Hostease客户咨询建站服务。他想要用Joomla建站。Hostease提供免费安装Joomla CMS服务。这可以让客户搭建网站变得更加简单和高效。下面是针对Joomla建站的一些使用心得。 Joomla CMS是一款开放自由的软件&#xff0c;为用户提供了创建和维护网站的自由度。它经过全…

基于.NET WinForms 数据CURD功能的实现

使用开发工具 VS 2022 C#&#xff0c;数据库MS SQL SERVER 2019 &#xff0c;基于NET WinForms&#xff0c;实现数据记录的创建(Create)、更新(Update)、读取(Read)和删除(Delete)等功能。主要控件包括&#xff1a;DataGridView&#xff0c;SqlDataApater &#xff0c; DataTab…

从面试官视角出发,聊聊产品经理的面试攻略

一、请进行自我介绍 这题基本是面试的开胃菜了&#xff0c;估计面试多的&#xff0c;自己答案都能倒背如流啦。 其实自我介绍还是蛮重要的&#xff0c;对我来说主要有 3 个作用&#xff1a;面试准备、能力预估、思维评估。 面试准备&#xff1a;面试官每天都要面 3 ~6 人&am…