Linux之模拟shell命令行解释器

news2024/12/25 8:55:48

文章目录

  • 前言
  • 一、输出提示符
    • 1.实际
    • 2.模拟
  • 二、输入指令、获取指令
    • 1.实际
    • 2.模拟
  • 三、fork创建子进程
  • 四、内建命令
  • 五、代码实现
  • 总结


前言

本文是基于前面介绍过的关于进程创建、进程终止、进程等待、进程替换等知识,尝试做的一个简单的shell命令解释器。


一、输出提示符

1.实际

在这里插入图片描述

2.模拟

printf("用户名@主机名 当前路径#");
fflush(std);

此处没有“\n”,会有缓冲区的问题,因此要用fflush(std);来刷新缓冲区

二、输入指令、获取指令

1.实际

在这里插入图片描述

2.模拟

  1. 输入
char lineCommand[NUM];
char* s = fgets(lineCommand,sizeof(lineCommand) - 1,stdin);
assert(s != NULL);
lineCommand[strlen(linCommand) - 1] = 0;

fgets函数获取一行的内容,将内容存在字符数组lineCommand中。
因为fgets获取也会将enter作为'\n’获取,为了避免多打印一行,我们要将最后一个元素重置为'\0'

  1. 获取
strtok(lineCommand," ");

用strtok函数将输入的字符串切割成若干个子串;

strtok函数的参数:第一次传字符串的首地址,之后传NULL即可(会从上次切割的位置继续切割)。

三、fork创建子进程

利用fork创建子进程;
替换函数需要带上v和p,即execvp函数。

为啥要用子进程去执行命令?
答:如果不创建子进程,而是让bash直接去执行进程,会将我们的bash直接替换为其他程序,shell就不能继续正常执行其他指令了(就回不到输入界面了)。

四、内建命令

我们在运行自己写的shell,输入cd ../cd path等命令时,发现路径并没有发生修改,这是为啥?
答:因为我们自己写的shell,执行很多命令都要用fork创建子进程,让子进程去执行。当子进程执行cd命令时,更改的时子进程的工作目录而与父进程无关,因此父进程的路径并不会发生修改。
因此,对于cd命令我们应该用内建命令:该命令不需要子进程执行,而是让bash自己执行。要修改程序的工作目录需要用chdir系统调用。

什么是当前路径?
当前路径就是cwd。
cwd -> 当前进程所在的工作目录(默认是文件存储在磁盘的路径);
exe -> 当前运行的是磁盘路径下的哪个进程。
更改当前进程的工作目录:chdir。(谁调用我,我就更改谁的工作目录)

在这里插入图片描述

五、代码实现

1 #include<stdio.h>
  2 #include<assert.h>
  3 #include<string.h>
  4 #include<unistd.h>
  5 #include<sys/types.h>
  6 #include<sys/wait.h>
  7 #include<stdlib.h>
  8 #define NUM 1024
  9 #define OPT_NUM 64
 10 char lineCommand[NUM];
 11 char* myargv[OPT_NUM];
 12 int lastCode = 0;
 13 int lastSig = 0;
 14 int main()
 15 {
 16         while(1)
 17         {
 18         printf("用户名@主机名 路径#");
 19         fflush(stdout);
 20         char* s = fgets(lineCommand,sizeof(lineCommand) - 1, stdin);
 21         assert(s != NULL);
 22         (void)s; 
 23         lineCommand[strlen(lineCommand) - 1] = 0;
 24         myargv[0] = strtok(lineCommand," "); 
 25         int i = 1;
 26         if(myargv[0] != NULL && strcmp(myargv[0], "ls") == 0)
 27         {
 28                 myargv[i++] = (char*)"--color=auto";
 29         }       
 30         while(myargv[i++] = strtok(NULL," "));
 31         if(myargv[0] != NULL && strcmp(myargv[0], "cd") == 0)
 32         {
 33                 if(myargv[1] != NULL) chdir(myargv[1]);
 34                 continue;
 35         }       
 36         if(myargv[0] != NULL && strcmp(myargv[0], "echo") == 0)
 37         {
 38                 if(strcmp(myargv[1], "$?") == 0)
 39                 {
 40                         printf("%d %d\n", lastCode, lastSig);
 41                 }       
 42                 else
 43                 {
 44                         printf("%s\n",myargv[1]);
 45                 }
 46                 continue;
 47         }
 48         #ifdef DEBUG
 49         for(int i = 0; myargv[i]; ++i)
 50         {
 51                 printf("myargv[%d]=%s\n", i, myargv[i]);
 52         }
 53         #endif
 54         pid_t id = fork();
 55         if(id < 0)
 56         {
 57                 perror("fork");
 58                 exit(1);
 59         }
 60         else if(id == 0)
 61         {
 62                 //
 63                 execvp(myargv[0], myargv);
 64                 exit(1);
 65         }
 66         int status = 0;
 67         pid_t ret = waitpid(id, &status, 0);
 68         assert(ret > 0);
 69         (void)ret;
 70         lastSig = WIFEXITED(status);
 71         lastCode = WEXITSTATUS(status);
 72         }
 73         return 0;
 74 }

运行:
在这里插入图片描述
文件tt.c
在这里插入图片描述
在这里插入图片描述


总结

以上就是今天要讲的内容,本文介绍了如何实现一个简单的shell解释器。本文作者目前也是正在学习Linux相关的知识,如果文章中的内容有错误或者不严谨的部分,欢迎大家在评论区指出,也欢迎大家在评论区提问、交流。
最后,如果本篇文章对你有所启发的话,希望可以多多支持作者,谢谢大家!

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

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

相关文章

OpenCV实战(25)——3D场景重建

OpenCV实战&#xff08;25&#xff09;——3D场景重建 0. 前言1. 重建 3D 场景1.1 3D 场景点重建1.2 算法原理 2. 分解单应性3. 光束平差法4. 完整代码小结系列链接 0. 前言 在《相机姿态估计》一节中&#xff0c;我们学习了如何在校准相机时恢复观察 3D 场景的相机的位置。算…

TypeScript的10个缺点

文章目录 1. 语法繁琐2. 难以集成到一些工作流程3. 学习成本高4. 代码量多5. 编译时间长6. 在小型项目中无必要性7. 可读性降低8. 抽象层次增加9. 缺少类型定义10. 生态系统 1. 语法繁琐 TypeScript 的类型注解、泛型等语法增加了代码的复杂度和学习难度&#xff0c;对小型项目…

LC-1130. 叶值的最小代价生成树(贪心、区间DP、单调栈)

1130. 叶值的最小代价生成树 难度中等272 给你一个正整数数组 arr&#xff0c;考虑所有满足以下条件的二叉树&#xff1a; 每个节点都有 0 个或是 2 个子节点。数组 arr 中的值与树的中序遍历中每个叶节点的值一一对应。每个非叶节点的值等于其左子树和右子树中叶节点的最大…

chatgpt赋能python:Python中的逆序操作

Python 中的逆序操作 在 Python 中&#xff0c;逆序&#xff08;reverse&#xff09;操作指的是将一个序列的元素顺序反转&#xff0c;也即将序列中最后一个元素变成第一个&#xff0c;倒数第二个元素变成第二个&#xff0c;以此类推。逆序有很多实际用途&#xff0c;比如根据…

基于C语言的平衡二叉树操作(包含完整代码)

平衡二叉树的定义: 为避免树的高度增长过快&#xff0c;降低二叉排序树的性能&#xff0c;规定在插入和删除二叉树结点时&#xff0c;要保证任意结点的左、右子树高度差的绝对值不超过1&#xff0c;将这样的二义树称为平衡二叉树AVL (Balanced Binary Tree),简称平衡树。 平衡…

【源码解析】流控框架Sentinel源码深度解析

前言 前面写了一篇Sentinel的源码解析&#xff0c;主要侧重点在于Sentinel流程的运转原理。流控框架Sentinel源码解析&#xff0c;侧重点在整个流程。该篇文章将对里面的细节做深入剖析。 统计数据 StatisticSlot用来统计节点访问次数 SpiOrder(-7000) public class Statis…

PCL 改进点云双边滤波算法

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 我们先来回顾一下之前该算法的计算过程,在二维图像领域中,双边滤波算法是通过考虑中心像素点到邻域像素点的距离(一边)以及像素亮度差值所确定的权重(另一边)来修正当前采样中心点的位置,从而达到平滑滤波效果。…

PHPMySQL基础(五):模拟登录后跳转+会话存储功能实现

PHP&MySQL基础&#xff08;一&#xff09;:创建数据库并通过PHP进行连接_长风沛雨的博客-CSDN博客 PHP&MySQL基础&#xff08;二&#xff09;:通过PHP对MySQL进行增、删、改、查_长风沛雨的博客-CSDN博客 PHP&MySQL基础&#xff08;三&#xff09;:处理查询SQL返…

一图看懂 tqdm 模块:一个可在循环和命令行中使用的快速、可扩展的进度条,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 tqdm 模块&#xff1a;一个可在循环和命令行中使用的快速、可扩展的进度条&#xff0c;资料整理笔记&#xff08;大全&#xff09; &#x1f9ca;摘要&#x1f9ca;模块图&…

软考高级架构师笔记-5计算机网络

目录 1. 前言 & 考情分析2. 网络功能和分类2.1 通信技术3. OSI七层模型及协议3. 1 局域网和广域网协议3. 2 协议3. 3 交换技术、路由、传输介质4 IP地址5 网络存储技术6 其它考点8. 结语1. 前言 & 考情分析 前文回顾: 软考高级架构师笔记-1计算机硬件软考高级架构师笔…

chatgpt赋能python:Python中未定义变量的默认值

Python中未定义变量的默认值 在Python编程中&#xff0c;有时候我们会使用未经定义的变量。如果这些变量没有被定义&#xff0c;那么它们将没有任何值。在这篇文章中&#xff0c;我们将讨论Python中未定义变量默认值的问题&#xff0c;并深入研究为什么这些默认值如此重要。 …

华为OD机试真题B卷 Java 实现【寻找关键钥匙】,附详细解题思路

一、题目描述 小强正在参加《密室逃生》游戏&#xff0c;当前关卡要求找到符合给定 密码K&#xff08;升序的不重复小写字母组成&#xff09;的箱子&#xff0c;并给出箱子编号&#xff0c;箱子编号为1~N。 每个箱子中都有一个字符串s&#xff0c;字符串由大写字母&#xff0…

改进YOLOv5,利用HRNet高分辨率特征金字塔的全新物体检测突破

目录 一、介绍1、物体检测的背景与重要性2、HRNet和YOLOv5的概述&#xff08;1&#xff09;HRNet的概述&#xff08;2&#xff09;YOLOv5的概述 二、HRNet的架构1、HRNet的基本单元2、HRNet的高分辨率特征金字塔3、HRNet的体系结构4、HRNet的特点5、HRNet的局限性 三、YOLOv5的…

chatgpt赋能python:Python中转化为列表的详细介绍

Python中转化为列表的详细介绍 Python是一门高级编程语言&#xff0c;它使用起来简单易学&#xff0c;被广泛应用于大数据处理、科学计算、机器学习等领域。在Python编程中&#xff0c;列表是一种非常重要的数据结构&#xff0c;它允许我们存储和操作一组数据&#xff0c;并且…

jenkins —— pipeline基础语法与示例

一、Jenkins介绍 二、Jenkins Pipeline介绍 Jenkins Pipeline总体介绍 1.Pipeline 是Jenkins 2.X核心特性&#xff0c;帮助Jenkins实现从CI到CD与DevOps的转变 2.Pipeline 简而言之&#xff0c;就是一套运行于Jenkins上的工作流框架&#xff0c;将原本独立 运行于单个或者多个…

GPT-4 的 6 个最佳使用场景

https://www.howtogeek.com/884077/best-uses-for-chatgpt-4/ 作者&#xff1a;SYDNEY BUTLER 无论是在 ChatGPT 中还是通过 API&#xff0c;对 OpenAI 的 GPT-4 模型的访问比 GPT-3.5 限制更多。这意味着你需要慎重考虑在何种情况下使用 GPT-4&#xff0c;并选择性地将最适合…

浙大知识图谱基础:学习笔记

0 基础知识 知识图谱中&#xff0c;知识的结构化表示主要有符号表示和向量表示两类方法。符号表示包括&#xff1a;一阶谓词逻辑&#xff0c;语义网络&#xff0c;描述逻辑和框架系统等。当前主要采用基于图的符号化知识表示&#xff0c;最常用的是有向标记图。 有向标记图分为…

SpringBoot统一功能处理(统一处理用户登陆权限验证、统一异常处理以及统一数据返回格式)

目录 1. SpringBoot统一功能处理简介 2. 统一处理用户登陆验证 2.1 原生SpringAOP实现统一登陆验证的问题 2.2 Spring拦截器实现用户统一登陆验证 2.3 扩展: 统一访问前缀添加 3. 统一异常处理 4. 统一数据返回格式 4.1 统一数据返回格式的必要性 4.2 实现统一数据返…

C++ vector类成员函数介绍

目录 &#x1f914;vector模板介绍&#xff1a; &#x1f914;特点&#xff1a; &#x1f914;vector的成员函数&#xff1a; &#x1f50d;vector构造函数&#xff1a; &#x1f50d;vector赋值函数 &#x1f50d;vector容器的判断函数 resize函数的重点内容&#xff1a; …

chatgpt赋能python:Python中的并:优化代码执行效率的利器

Python中的并&#xff1a;优化代码执行效率的利器 Python作为一种优秀的编程语言&#xff0c;被广泛使用于各种各样的项目中。然而&#xff0c;随着程序的不断扩张&#xff0c;代码的复杂度日益增加&#xff0c;执行效率也愈加遇到了严峻的挑战。在这种情况下&#xff0c;Pyth…