【Linux】模拟实现bash(简易版)

在这里插入图片描述

👦个人主页:Weraphael
✍🏻作者简介:目前正在学习c++和算法
✈️专栏:Linux
🐋 希望大家多多支持,咱一起进步!😁
如果文章有啥瑕疵,希望大佬指点一二
如果文章对你有帮助的话
欢迎 评论💬 点赞👍🏻 收藏 📂 加关注😍


目录

  • 前言
  • 一、用户输入
  • 二、指令分割
  • 三、程序替换
      • 3.1 外部命令
      • 3.2 内建命令
        • 3.2.1 cd
        • 3.2.2 export
        • 3.2.3 echo
  • 四、总结及源码

前言

简单回顾一下往期知识,命令行解释器bash只是一个”外壳程序",而操作系统则称为“内壳程序”,这是因为操作系统不相信用户,因此我们用户只能通过“外壳程序”将指令进行翻译给操作系统,操作系统再将结果通过“外壳程序”返回给用户。

请添加图片描述

以上图片来自于【往期博客】

由于目前学习到的知识有限,后面会慢慢更新相关接口 ~

一、用户输入

首先命令行bash需要提示类似于:[用户名@主机名 当前目录]$。我们可以使用以下系统调用接口来获取它们:

  1. 获取用户名
#include <unistd.h>
char *getlogin();
  1. 获取主机名
#include <unistd.h>
int gethostname(char *name, size_t len);

其中:

  • 该函数的功能是将主机名复制到 name 指向的缓冲区中(字符数组),注意name 缓冲区应该足够大以容纳主机名。

  • 第二个参数len 是缓冲区的长度(数组长度)。

  • 返回值:

    • 成功返回0,并将主机名复制到 name 指向的缓冲区中。
    • 失败返回 -1
  1. 获取当前工作目录路径
#include <unistd.h>
char *getcwd(char *buf, size_t size);

其中:

  • 该函数的功能是将当前工作目录的绝对路径复制到 buf 指向的缓冲区中,并保证以空字符 \0 结尾。注意:传递给 getcwd() 的缓冲区应该足够大。

  • size 参数表示缓冲区的大小。

  • 函数的返回值:

    • 如果成功, buf 指向的缓冲区地址。
    • 如果失败,返回 NULL

有了以上接口,我们就可以用代码来实现了

请添加图片描述

接下来就应该轮到用户输入指令,本质就是输入字符串。

这里需要注意的是,由于我们输入的指令可以带选项,那么必定是带空格的(如ls -al),而 scanf默认遇到空格或者换行就不读取了。除非你使用修饰符配合scanf函数

char str[100];
scanf("%[^\n]", str); // 可以读空格和换行

除以上方法以外,fgets函数也可以读取空格和换行

#include <stdio.h>
char *fgets(char *s, int size, FILE *stream);

其中:

  • 第一个参数:用于存储从输入流中读取的数据。一般是一个字符数组。
  • 第二个参数:计算整个字符数组的大小。
  • 第三个参数:这个参数指定了从哪个文件流中读取数据。在大多数情况下,我们使用标准输入流,即键盘输入,因此会传递stdin
  • 返回值:读取数据成功时返回一个指向目标缓冲区的指针,如果读取失败或者到达文件结尾时返回NULL

请添加图片描述

请添加图片描述

以上程序还有缺陷,那就是当我们输入完一条指令后,bash把结果返回给我们后,会继续重复提示我们输入指令。而我们目前写的程序执行完一条指令后就退出进程。因此,以上程序应当是一个循环。

请添加图片描述

请添加图片描述

二、指令分割

当用户输入完指令,我们要进行指令分割为后面【程序替换】做准备。

由于一开始我们使用fgets函数将最后的回车\n给读取到了command数组,因此我们要将其去掉(置为'\0'即可)

请添加图片描述

接下来我们进行分割命令行参数,C语言提供了字符串分割函数 strtok。当然你也可以自己手撕一个hh

#include <string.h>
char *strtok(char *str, const char *delim);

其中:

  • str:要分割的字符串,第一次调用时传入待分割的字符串,后续调用传NULL继续分割该字符串,函数会继续在上一次调用的字符串中查找下一个标记的位置。
  • delim:分隔符的字符串,即用来确定标记边界的字符集合。
  • strtok() 函数返回一个指向分割后的标记的指针,如果没有找到标记,则返回NULL

请添加图片描述

请添加图片描述

三、程序替换

3.1 外部命令

对于外部命令,shell则会创建一个子进程,并在子进程中进行程序替换来执行这些命令。在执行完成后,Shell会等待子进程退出,并获取子进程的退出码。

请添加图片描述

如上所示,有很多替换函数供我们选择,为了方便,尽量不要选择带l,因为我们已经将命令行参数分割好了在字符指针数组argv中,而无需一一列举;另外,我们也不要选择不带p的,因为这样还需要我们自己去写完整的文件路径。

综上,我们可以使用execvpe函数。另外,environ是全局变量它是由标准C库提供的,当用户登录时,shell会读取用户目录下的.bash_profile文件,里面保存了导入环境变量的方式。

请添加图片描述

请添加图片描述

如上所示,我们执行的命令确实起效了,但是还是有些缺陷,比如ls显示出来的文件没有高亮;以及ll(ls -l重命名)没有效果,因此我们的代码还是可以再改造改造。

我们可以先来解决ls显示的文件没有高亮的问题

请添加图片描述

因此,我们只需要对argv数组添加一个命令行参数,也就是--color=auto即可

请添加图片描述

请添加图片描述

最后来解决ll未显示出结果的问题。

请添加图片描述

请添加图片描述

3.2 内建命令

什么是内建命令呢?比如以cd为例,子进程执行cd命令改变了子进程的工作目录,由于父子进程是相互独立的,子进程改变了,而父进程bash却没有影响。因此,内建命令是不需要通过创建子进程来执行。

Linux中有很多内建命令:

请添加图片描述

这里我只挑选一些来完善

3.2.1 cd

我们可以使用系统调用接口chdir函数来改变当前进程的工作目录,并且它对于特殊的路径 .. 也可以完成对应的更改,但除了cd ~cd -,分别是返回家目录和返回最近一次访问的目录,注意:家目录和最近一次访问目录可以通过环境变量来获取。

请添加图片描述

请添加图片描述

但需要注意的是:改变当前进程的工作目录不会直接影响环境变量 PWD,我们需要手动更新。(以上环境变量只截取了部分)

步骤如下:

  1. 调用getcwd函数更新pwd数组

  2. pwd替换掉原来环境变量PWD的值即可

我们可以使用sprintf函数来替换。sprintf 函数是 C 语言中的一个标准库函数,用于将格式化的数据写入一个字符串中。

#include <stdio.h>
int sprintf(char *str, const char *format, ...);

其中:

  • str 是一个指向字符数组的指针,指向需要修改的字符串
  • 后面的参数就和printf函数一样了

请添加图片描述

请添加图片描述

(以上环境变量只截取了部分)

3.2.2 export

这个看似非常简单,比如添加环境变量export x=333,那么直接使用putenv(argv[1])(其中argv[0]表示exportargv[1]表示x=333

如果你是以上这样做法导致第一次添加可能成功,但第二次添加后,第一次添加的就没了。这是因为argv[1] 中的内容是不断变化的,第二次添加就覆盖了第一次添加。

正确做法:

  • 一般用户自定义的环境变量,在 bash 中需要用户自己维护一个字符指针数组

  • 先将待添加的环境变量拷贝至指针数组

  • 再从中读取,并调用 putenv 函数添加至环境变量表

请添加图片描述

请添加图片描述

3.2.3 echo
  • echo首先需要能获取最近一次进程的退出状态

请添加图片描述

本应当返回ls进程的退出状态,而他原原本本返回了$?

请添加图片描述

  • 我们打印环境变量,例如$PATH会出现什么都没输出的现象

请添加图片描述

请添加图片描述

请添加图片描述

  • 输出字符串会带双引号的情况

请添加图片描述

四、总结及源码

所谓的shell也是一个进程,它可以获取用户的输入,然后对用户的输入做分析。对于内建命令,shell会直接调用函数来执行;而对于外部命令,shell则会创建一个子进程,并在子进程中进行进程替换来执行相对应的命令。在执行完成后,shell会等待子进程退出,并获取子进程的退出码。

  • 获取源码:点击跳转

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

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

相关文章

视频号小店应该如何开店呢?详细的开店流程分享给你!

大家好&#xff0c;我是电商小V 视频号小店就是威信视频号团队为咱们商家提供的卖货平台&#xff0c;可以说是支持咱们商家在视频号场景中开店进行经营的模式&#xff0c; 视频号大概的开店流程那就是&#xff1a;找到视频号开店&#xff0c;选择企业入驻&#xff0c;填写信息&…

5.9代码

1.选素数 从数学来看&#xff0c;n素数*k,k2,3,4...&#xff0c;而我们要进行两次&#xff0c;由于有多个解时我们要选最小的&#xff0c;所以要找到最大的素数&#xff0c;但是这个最大素数要小于等于n/2的整数&#xff0c;然后中间那一次的n的选择要选比最大素数大的最少的合…

【二分查找 滑动窗口】100257找出唯一性数组的中位数

本文涉及知识点 二分查找算法合集 C算法&#xff1a;滑动窗口总结 LeetCode 100257找出唯一性数组的中位数 给你一个整数数组 nums 。数组 nums 的 唯一性数组 是一个按元素从小到大排序的数组&#xff0c;包含了 nums 的所有非空子数组中不同元素的个数。 换句话说&#xf…

java图片水印字体乱码问题

问题描述&#xff1a;在linux Centos-7.5_64bit系统的其他服务器上不乱码&#xff0c;在部署项目的正式服务器乱码 水印字体设置是 微软雅黑 Font wordFont new Font("微软雅黑", Font.ITALIC,(srcImgHeightsrcImgWidth)/50); 一.Springboot项目&#xff0c;部署在…

高效视频剪辑:批量剪辑添加srt字幕,快速制作专业视频

在视频制作过程中&#xff0c;字幕扮演着至关重要的角色&#xff0c;它们不仅能增强观众对视频内容的理解&#xff0c;还能提高视频的观感体验。然而&#xff0c;手动为每一个视频添加字幕是一项既耗时又繁琐的任务。现在有了云炫AI智剪和技巧&#xff0c;我们可以轻松地实现批…

Photoshop中选区工具的应用

Photoshop中选区工具的应用 前言Photoshop中选区工具的基本操作创建选区的工具及方法选择、取消、隐藏选区选区的增加、减少选区的应用变换扩大选取与选取相似 Photoshop中采用快速选择工具来创建选区Photoshop中采用色彩范围命令来创建选区Photoshop中采用快速蒙版来创建选区P…

安全加固

目录 1.文件锁定管理 2.设置用户账户有效期 3.查看并清除命令历史记录 4.设置用户超时登出时间 5.用户切换 6.用户提权 7.禁用重启热键CtrlAltDel 8.设置单用户模式密码 9.调整BIOS引导设置 10.禁止root用户从本地登录&#xff1a; 11.禁止root用户通过ss…

大数据------JavaWeb------Tomcat(完整知识点汇总)

Web服务器——Tomcat Web服务器定义 它是一个应用程序&#xff08;软件&#xff09;&#xff0c;对HTTP协议的操作进行封装&#xff0c;使得程序员不必直接对协议进行操作&#xff0c;让Web开发更便捷 Web服务器主要功能 封装HTTP协议操作&#xff0c;简化开发将Web项目部署到…

如何免费获得进仓数据库专家认证(帮你省50块钱)

这篇文章分三个部分 50块钱解决&#xff08;全靠自己钱可能打水漂考试只有三次机会&#xff09;50块钱解决&#xff08;全靠自己考试只有三次机会。&#xff09;30块钱解决&#xff08;考试靠我&#xff0c;报名费帮你0元处理&#xff0c;要求只有在线大学生。能力有限只能考K…

春秋云镜 CVE-2022-4230

靶标介绍&#xff1a; WP Statistics WordPress 插件13.2.9之前的版本不会转义参数&#xff0c;这可能允许经过身份验证的用户执行 SQL 注入攻击。默认情况下&#xff0c;具有管理选项功能 (admin) 的用户可以使用受影响的功能&#xff0c;但是该插件有一个设置允许低权限用户…

多标签分割

https://github.com/PaddlePaddle/PaddleSeg/blob/release/2.9/configs/multilabelseg/README_cn.md

Adobe Photoshop PS 25.6.0 解锁版 (最流行的图像设计软件)

前言 Adobe Photoshop 是一款专业强大的图片处理工具&#xff0c;从照片编辑和合成到数字绘画、动画和图形设计&#xff0c;一流的图像处理和图形设计应用程序是几乎每个创意项目的核心所在。利用 Photoshop 在桌面上的强大功能&#xff0c;您可以在灵感来袭时随时随地进行创作…

3分钟快速了解VR全景编辑器

说到VR全景&#xff0c;想必大多数人都见过那种可以360旋转拖动观看的图片。虽然这种技术已经不算新鲜&#xff0c;如果你以为这就是VR全景的全部&#xff0c;那就大错特错了&#xff01; 上面看到的这种形式&#xff0c;只能算VR全景的第一层形态。现在的VR全景已经发展成为了…

Lobe Chat–在线AI对话聊天机器人,一键部署,免费开源

Lobe Chat 现代化设计的开源 ChatGPT/LLMs 聊天应用与开发框架 支持语音合成、多模态、可扩展的&#xff08;function call&#xff09;插件系统 一键免费拥有你自己的 ChatGPT/Gemini/Claude/Ollama 应用 项目演示 支持多种模型接口 支持语音输入输出 支持云端同步 丰富多彩非…

如何在电脑桌面显示此电脑

如何在电脑桌面显示此电脑 鼠标在桌面空白处点击右键展示个性化 选择主题 选择桌面图标设置

Sqli-labs第五,六关

目录 首先找到他们的闭合方式 操作 总结&#xff1a; 第五关根据页面结果得知是字符型但是和前面四关还是不一样是因为页面虽然有东西。但是只有对于请求对错出现不一样页面其余的就没有了。这个时候我们用联合注入就没有用&#xff0c;因为联合注入是需要页面有回显位。如果…

OpenCompass笔记

假设一个模型&#xff0c;被2bit量化&#xff0c;然后一直瞎说话&#xff0c;怎么办&#xff1f;你是不是应该评估一下这个模型的效果&#xff1f; 但是&#xff0c;大模型的评估是很复杂的&#xff0c;如果说小模型的测试就像体检&#xff0c;指标明确&#xff0c;那么大模型…

数据结构_顺序表中基本操作的实现_代码

学习笔记&#xff0c;仅供参考 1.头文件 2.初始化 3.增加值 4.根据下标取值 5.查找 6.插入 7.删除 8.动态增加数组的长度 9.所有代码 10.运行结果 1.头文件 //顺序表的实现——动态分配 #include<stdio.h> #include<stdlib.h> #define InitSize 10 type…

国产银河麒麟V10SP1系统下搭建TiDB数据库操作步骤图文

开发目的&#xff1a;在国产银河麒麟系统中搭建TiDB数据库运行环境。 开发工具&#xff1a;银河麒麟系统V10SP1TiDBMySql数据库8.0。 具体步骤&#xff1a; 1、在VmWare虚拟机中安装好国产银河麒麟V10Sp1操作系统。 2、打开终端命令&#xff0c;安装TiDB相关软件&#xff1…

LearnOpenGL(十一)之光源

一、投光物 将光投射(Cast)到物体的光源叫做投光物(Light Caster)。 二、平行光 当一个光源处于很远的地方时&#xff0c;来自光源的每条光线就会近似于互相平行&#xff0c;我们可以称这些光为平行光。当我们使用一个假设光源处于无限远处的模型时&#xff0c;它就被称为定向…