【Linux庖丁解牛】—自定义shell的编写!

1. 打印命令行提示符

在我们使用系统提供的shell时,每次都会打印出一行字符串,这其实就是命令行提示符,那我们自定义的shell当然也需要这一行字符串。

这一行字符串包含用户名,主机名,当前工作路径,所以,我们在打印这行字符串时,需要获取这些信息。根据我们之前学过的知识,我们可以用getenv系统调用来获取!

这里有一个接口gethostname,我试过了用getenv来获取系统的主机名,但是,在我的系统上似乎无法获取,这可能和系统有关,但是我们用gethostname这个接口也可以很安全的获得主机名,具体用法用man手册看一看也就会了。 

 结果没有问题:

2. 获取用户输入

当我们解决了命令行提示符的问题后,接下来我们就会注意到每次执行指令都会有一个光标在闪烁等待用户输入指令!所以我们现在就要解决这个问题!

如果用scanf来获取缓冲区的字符串坑定是不行的,因为scanf默认以空格作为分隔符,而我们在输入指令带选项时,就会有空格!

那我们就用fgets:

 可是为什么回显时有两次换行呢?

原因含很简单:我们在输入指令时,最后输入的换行也在缓冲区中被fges获取保留在数组commandline的最后一个字符,解决方法也很简单,只需要把最后一个字符置为0即可!

现在写的代码还是不够优雅,我们稍微封装一下:

  1 #include <iostream>2 #include <cstdlib>3 #include <cstdio>4 #include <unistd.h>5 #include <cstring>6 7 using namespace std;8 9 #define COMMAND_SIZE 102410 #define FORMAT "%s@%s:%s$ "11 12 const char* get_user_name()13 {14     const char* user=getenv("USER");15     return user==NULL?"NONE":user;16     //return user;17 }18 19 const char* get_pwd()20 {21     const char* pwd=getenv("PWD");22     return pwd==NULL?"NONE":pwd;23 }24 25 //制作命令行提示符Command Prompt26 void make_command_prompt(char cmd_prompt[],int size)27{28     char hostname[256];29     gethostname(hostname,sizeof(hostname));30     snprintf(cmd_prompt,size,FORMAT,get_user_name(),hostname,get_pwd());31 }                                                                                                                                               32 33 //打印命令行提示符34 void print_cmd_prompt()35 {36     char prompt[COMMAND_SIZE];37     make_command_prompt(prompt,sizeof(prompt));38     printf("%s",prompt);39     fflush(stdout);40 }41 42 //获取用户输入的命令43 bool get_command(char* out,int size)44 {45     char* c=fgets(out,size,stdin);46     if(c==NULL) return false;47     out[strlen(out)-1]=0;48     //如果用户什么都没有输入则返回false49     if(strlen(out)==0) return false;50     return true;51 }52 53 int main()54 {55     //1.打印命令行提示法56     print_cmd_prompt();57     //2.获取用户输入的命令58     char commandline[COMMAND_SIZE];59     if(get_command(commandline,sizeof(commandline)))60     {61         printf("%s\n",commandline);62     }63     return 0;64 }

我们使用的shell是不断在获取用户的指令的,也就是说shell一旦跑起来就是一个死循环,直到我们退出shell!所以我们还应该将我们的主体逻辑改一下!

3. 解析命令行

我们获取了用户输入的字符串后【ls -a -l】,我们不可能用这一长串字符串去执行我们的指令,我们需要做的下一步就是将我们获取的字符串按空格切割!具体如何做到如下:

我们先在全局定义一个命令行参数表char*  g_argv[MAXARGC]来记录我们切割的命令行参数

接下来,我们封装一个函数来完成我们的切割任务:

测试函数:

 测试结果:

4. 执行命令 

执行命令也非常简单,这需要用到我们之前学过的知识,创建子进程,将子进程进行程序替换!

5.简化工作路径的显示 

通过上图我们可以观察到我们自定义的shell显示的工作路径太长了,为了和原shell尽可能保持一致,所以我们封装一个函数来解决这个问题!

 6. 检测并处理内建命令

我们在输入ls,pwd等命令时,我们自定义的shell雀氏可以很好的帮我们完成工作。但是,当我们输入cd,export等命令时,此时的shell就不再适用了。cd命令是改变当前的工作路径,但是我们自定义的shell是子进程通过进程替换的方式帮我们执行命令,而cd这类命令是去环境变量表中那到当前的工作路径,我们需要更改父进程bash的环境变量。所以对于cd这类的命令,我们需要用父进程去执行。而cd这类的命令我们又称为内建命令,因此,在执行命令之前,我们需要一个检测并处理内建命令的操作!

 

 下面是测试结果:

 我们发现工作路径果然发生改变了,但是命令行显示的路径为什么没有发生改变呢?

但cd命令执行时,先是进程的工作路径发生改变,然后环境变量中记录的工作路径再改变,而这个工作也是由shell来完成的,但是目前我们的自定义shell还没有实现这个功能!并且,我们获取当前工作路径是通过获取环境变量的方式拿到的,所以我们在命令行中显示的工作路径永远是久的!

因此,获取当前工作路径有一个更好的方式->系统调用【getcwd】!

下面的测试就符合预期了! 

但是,环境变量中的pwd是实实在在发生了变化的,所以我们自定义的shell也应该实现这一个功能!

所以,我们仅需要在获取当前工作路径之后,用puenv导入到环境变量中即可!

 当然,还有许多内建命令,比如echo,我们可以完善这些内建命令,这里就不写了【比较懒】。

7. 完善环境变量表

目前这里自定义的shell只有命令行参数表,还缺少一张环境变量表。父进程bash在启动时,从配置文件中获取环境变量,子进程则继承父进程的环境变量。如果我们要模拟bash获取环境变量的方式,就必须从配置文件中那数据。但是,这里目前是做不到的【没办法到配置文件中拿数据】。

不过,我们自定义的shell本质上还是bash的子进程,所以我们可以到父进程中获取环境变量!

 

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

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

相关文章

应用案例 | 设备分布广, 现场维护难? 宏集Cogent DataHub助力分布式锅炉远程运维, 让现场变“透明”

在日本&#xff0c;能源利用与环保问题再次成为社会关注的焦点。越来越多的工业用户开始寻求更高效、可持续的方式来运营设备、管理能源。而作为一家专注于节能与自动化系统集成的企业&#xff0c;日本大阪的TESS工程公司给出了一个值得借鉴的答案。 01 锅炉远程监控难题如何破…

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…

jdk同时安装多个版本并自由切换

一、安装不同版本的JDK 二、配置环境变量&#xff08;多版本JDK&#xff09; 1. 新建版本专用环境变量&#xff08;用于切换&#xff09; 操作位置&#xff1a;系统变量 > 新建 变量名&#xff1a;JAVA_HOME_1.8 变量值&#xff1a;JDK 8安装路径变量名&#xff1a;JAVA1…

java中装饰模式

目录 一 装饰模式案例说明 1.1 说明 1.2 代码 1.2.1 定义数据服务接口 1.2.2 定义基础数据库服务实现 1.2.3 日志装饰器 1.2.4 缓存装饰器 1.2.5 主程序调用 1.3 装饰模式的特点 一 装饰模式案例说明 1.1 说明 本案例是&#xff1a;数据查询增加缓存&#xff0c;使用…

【论文阅读】YOLOv8在单目下视多车目标检测中的应用

Application of YOLOv8 in monocular downward multiple Car Target detection​​​​​ 原文真离谱&#xff0c;文章都不全还发上来 引言 自动驾驶技术是21世纪最重要的技术发展之一&#xff0c;有望彻底改变交通安全和效率。任何自动驾驶系统的核心都依赖于通过精确物体检…

在uni-app中如何从Options API迁移到Composition API?

uni-app 从 Options API 迁移到 Composition API 的详细指南 一、迁移前的准备 升级环境&#xff1a; 确保 HBuilderX 版本 ≥ 3.2.0项目 uni-app 版本 ≥ 3.0.0 了解 Composition API 基础&#xff1a; 响应式系统&#xff1a;ref、reactive生命周期钩子&#xff1a;onMount…

408第一季 - 数据结构 - 图

图的概念 完全图 无向图的完全图可以这么想&#xff1a;如果有4个点&#xff0c;每个点都会连向3个点&#xff0c;每个点也都会有来回的边&#xff0c;所以除以2 有向图就不用除以2 连通分量 不多解释 极大连通子图的意思就是让你把所有连起来的都圈出来 强连通图和强连通…

31.2linux中Regmap的API驱动icm20608实验(编程)_csdn

regmap 框架就讲解就是上一个文章&#xff0c;接下来学习编写的 icm20608 驱动改为 regmap 框架。 icm20608 驱动我们在之前的文章就已经编写了&#xff01; 因为之前已经对icm20608的设备树进行了修改&#xff0c;所以大家可以看到之前的文章&#xff01;当然这里我们还是带领…

Vue速查手册

Vue速查手册 CSS deep用法 使用父class进行限定&#xff0c;控制影响范围&#xff1a; <template><el-input class"my-input" /> </template><style scoped> /* Vue 3 推荐写法 */ .my-input :deep(.el-input__inner) {background-color…

振动力学:无阻尼多自由度系统(受迫振动)

本文从频域分析和时域分析揭示系统的运动特性&#xff0c;并给出系统在一般形式激励下的响应。主要讨论如下问题&#xff1a;频域分析、频响函数矩阵、反共振、振型叠加法等。 根据文章1中的式(1.7)&#xff0c;可知无阻尼受迫振动的初值问题为&#xff1a; M u ( t ) K u …

真实案例分享,Augment Code和Cursor那个比较好用?

你有没有遇到过这种情况&#xff1f;明明知道自己想要什么&#xff0c;写出来的提示词却让AI完全理解错了。 让AI翻译一篇文章&#xff0c;结果生成的中文不伦不类&#xff0c;机器僵硬&#xff0c;词汇不同&#xff0c;鸡同鸭讲。中国人看不懂&#xff0c;美国人表示耸肩。就…

zotero及其插件安装

zotero官网&#xff1a;Zotero | Your personal research assistant zotero中文社区&#xff1a;快速开始 | Zotero 中文社区 插件下载镜像地址&#xff1a;Zotero 插件商店 | Zotero 中文社区 翻译&#xff1a;Translate for Zotero 接入腾讯翻译API&#xff1a;总览 - 控制…

【SSM】SpringMVC学习笔记8:拦截器

这篇学习笔记是Spring系列笔记的第8篇&#xff0c;该笔记是笔者在学习黑马程序员SSM框架教程课程期间的笔记&#xff0c;供自己和他人参考。 Spring学习笔记目录 笔记1&#xff1a;【SSM】Spring基础&#xff1a; IoC配置学习笔记-CSDN博客 对应黑马课程P1~P20的内容。 笔记2…

从认识AI开始-----变分自编码器:从AE到VAE

前言 之前的文章里&#xff0c;我已经介绍了传统的AE能够将高维输入压缩成低维表示&#xff0c;并重建出来&#xff0c;但是它的隐空间结构并没有概率意义&#xff0c;这就导致了传统的AE无法自行生成新的数据&#xff08;比如新图像&#xff09;。因此&#xff0c;我们希望&a…

智慧赋能:移动充电桩的能源供给革命与便捷服务升级

在城市化进程加速与新能源汽车普及的双重推动下&#xff0c;移动充电桩正成为能源供给领域的一场革命。传统固定充电设施受限于布局与效率&#xff0c;难以满足用户即时、灵活的充电需求&#xff0c;而移动充电桩通过技术创新与服务升级&#xff0c;打破了时空壁垒&#xff0c;…

发版前后的调试对照实践:用 WebDebugX 与多工具构建上线验证闭环

每次产品发版都是一次“高压时刻”。版本升级带来的不仅是新功能上线&#xff0c;更常伴随隐藏 bug、兼容性差异与环境同步问题。 为了降低上线风险&#xff0c;我们逐步构建了一套以 WebDebugX 为核心、辅以 Charles、Postman、ADB、Sentry 的发版调试与验证流程&#xff0c;…

如何安装huaweicloud-sdk-core-3.1.142.jar到本地仓库?

如何安装huaweicloud-sdk-core-3.1.142.jar到本地仓库&#xff1f; package com.huaweicloud.sdk.core.auth does not exist 解决方案 # 下载huaweicloud-sdk-core-3.1.142.jar wget https://repo1.maven.org/maven2/com/huaweicloud/sdk/huaweicloud-sdk-core/3.1.142/huawe…

Python学习(7) ----- Python起源

&#x1f40d;《Python 的诞生》&#xff1a;一段圣诞假期的奇妙冒险 &#x1f4cd;时间&#xff1a;1989 年圣诞节 在荷兰阿姆斯特丹的一个寒冷冬夜&#xff0c;灯光昏黄、窗外飘着雪。一个程序员 Guido van Rossum 正窝在家里度假——没有会议、没有项目、没有 bug&#xf…

DiMTAIC 2024 数字医学技术及应用创新大赛-甲状腺B超静态及动态影像算法赛-参赛项目

参赛成绩 项目介绍 去年参加完这个比赛之后&#xff0c;整理了项目文件和代码&#xff0c;虽然比赛没有获奖&#xff0c;但是参赛过程中自己也很有收获&#xff0c;自己一个人搭建了完整的pipeline并基于此提交了多次提高成绩&#xff0c;现在把这个项目梳理成博客&#xff0c…

绘制饼图详细过程

QtCharts绘制饼图 说明&#xff1a;qcustomplot模块没有绘制饼图的接口和模块&#xff0c;所以用Qt官方自带的QtCharts进行绘制。绘制出来还挺美观。 1 模块导入 QT chartsQT_BEGIN_NAMESPACE以上这两行代码必须得加 2 总体代码 widget.h #ifndef WIDGET_H #defin…