Linux一学就会——编写自己的shell
编写自己的shell
进程程序替换
替换原理
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数
以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动
例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。

替换函数
其实有几种以exec开头的函数,统称exec函数:
#include <unistd.h>
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg, ...,char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
解释
exec是函数替换的开头,后面跟的都是多加的功能:
l:list的简写,表示参数采用列表。
p:path的简写,就是自动搜索并添加环境变量。可以使用环境变量PATH,无需写全路径。
v:vector的简写,是可以用参数数组。
e:environment的简写,就是环境变量。就是带e都要自己组装环境变量,而且是数组形式传入。
这些函数如果调用成功则加载新的程序从启动代码开始执行,不再返回。
如果调用出错则返回-1
所以exec函数只有出错的返回值而没有成功的返回值。

可变参数
我们刚刚可以看到int execl(const char *path, const char *arg, …);
比如:
int func(int, ... ) {...
}int main() {func(2, 2, 3);func(3, 2, 3, 4);
}
函数 func() 最后一个参数写成省略号,即三个点号(…),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:
定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
使用 int 参数和 va_start() 宏来初始化 va_list 变量为一个参数列表。宏 va_start() 是在 stdarg.h 头文件中定义的。
使用 va_arg() 宏和 va_list 变量来访问参数列表中的每个项。
使用宏 va_end() 来清理赋予 va_list 变量的内存。
也就是说可变参数是放在传入参数最后,放在中间必须在输入结束之后再输入一个NULL,而且可变参数和前面放的参数类型一致。
exec调用举例:
#include <unistd.h>
int main()
{
char *const argv[] = {"ps", "-ef", NULL};
char *const envp[] = {"PATH=/bin:/usr/bin", "TERM=console", NULL};
execl("/bin/ps", "ps", "-ef", NULL);
// 带p的,可以使用环境变量PATH,无需写全路径
execlp("ps", "ps", "-ef", NULL);
// 带e的,需要自己组装环境变量
execle("ps", "ps", "-ef", NULL, envp);
execv("/bin/ps", argv);
// 带p的,可以使用环境变量PATH,无需写全路径
execvp("ps", argv);
// 带e的,需要自己组装环境变量
execve("/bin/ps", argv, envp);
exit(0);
}
事实上,只有execve是真正的系统调用,其它五个函数最终都调用 execve,所以execve在man手册 第2节,其它函数在
man手册第3节。这些函数之间的关系如下图所示。

开始写自己的shell
用下图的时间轴来表示事件的发生次序。其中时间从左向右。shell由标识为sh的方块代表,它随着时间的流逝从左
向右移动。shell从用户读入字符串"ls"。shell建立一个新的进程,然后在那个进程中运行ls程序并等待那个进程结
束。
每当输入一个命令时,bash就会创建一个子进程来实现的要的命令进程,上述就是ls,等待子进程退出,主进程继续等待命令输入和读取命令,再创建子进程等…
第一步创建一个界面然后让他一直死循环
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>#define MAX_CMD 1024
char command[MAX_CMD];
int myshell_face()
{memset(command, 0x00, MAX_CMD);printf("[Tomshell$]#");fflush(stdout);if (scanf("%[^\n]%*c", command) == 0){getchar();return -1;}return 0;
}
int main(int argc, char *argv[])
{while (1) // shell主循环{myshell_face(); }return 0;
}
当然这个界面是可以输入命令的,但是你怎么输入都没用。
接下来是解析你输入的命令了。
把刚刚输入的命令行分析出来,比如遇到空格就会再次push_back命令行数组,当有空格就跳过空格,知道遇到NULL为止。
char **do_parse(char *command)
{int argc = 0;static char *argv[32];char *ptr = command;while (*ptr != '\0'){if (!isspace(*ptr))//如果不是空格就一直读取命令,直到遇到空格{argv[argc++] = ptr;while ((!isspace(*ptr)) && (*ptr) != '\0')//#include <ctype.h> isspace检测是否遇到空格{ptr++; //如果不是空格就一直读取命令,直到遇到空格}}else{while (isspace(*ptr))//如果命令前几个是空格就消除空格{//*ptr = '\0';//这句就不用加了ptr++;}}}argv[argc] = NULL;return argv;
}
解析完之后返回的是命令行参数数组指针
开始创建子进程并且用execvp替换子进程。
int do_exec(char *command)//进程替换函数=》用的就是exec
{char **argv = {NULL};int pid = fork(); // 一切形式的进程都让子进程去办,子进程就是白手套。if (pid == 0){argv = do_parse(command);if (argv[0] == NULL){exit(-1);}execvp(argv[0], argv); // 进程替换函数,可以添加环境变量p(path),参数格式是数组v(vector)} // 可以把exec当作call(goto)函数,exit当作return函数。else{waitpid(pid, NULL, 0);}return 0;
}
这样就可以在子进程实现命令行进程了。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>#define MAX_CMD 1024
char command[MAX_CMD];
int myshell_face()
{memset(command, 0x00, MAX_CMD);printf("[Tomshell$]#");fflush(stdout);if (scanf("%[^\n]%*c", command) == 0){getchar();return -1;}return 0;
}
char **do_parse(char *command)
{int argc = 0;static char *argv[32];char *ptr = command;while (*ptr != '\0'){if (!isspace(*ptr))//如果不是空格就一直读取命令,直到遇到空格{argv[argc++] = ptr;while ((!isspace(*ptr)) && (*ptr) != '\0')//#include <ctype.h> isspace检测是否遇到空格{ptr++; //如果不是空格就一直读取命令,直到遇到空格}}else{while (isspace(*ptr))//如果命令前几个是空格就消除空格{//*ptr = '\0';//这句就不用加了ptr++;}}}argv[argc] = NULL;return argv;
}
int do_exec(char *command)//进程替换函数=》用的就是exec
{char **argv = {NULL};int pid = fork(); // 一切形式的进程都让子进程去办,子进程就是白手套。if (pid == 0){argv = do_parse(command);if (argv[0] == NULL){exit(-1);}execvp(argv[0], argv); // 进程替换函数,可以添加环境变量p(path),参数格式是数组v(vector)} // 可以把exec当作call(goto)函数,exit当作return函数。else{waitpid(pid, NULL, 0);}return 0;
}
int main(int argc, char *argv[])
{while (1) // shell主循环{if (myshell_face() < 0)continue;do_exec(command);}return 0;
}
最终成果:

相关文章:
Linux一学就会——编写自己的shell
编写自己的shell 进程程序替换 替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行…...
编程练习【有效的括号】
给定一个只包括 (,),{,},[,] 的字符串 s ,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 每个右括号都有一个对应的相同类型的左…...
Android 音频开发——桌面小部件(七)
对于收音机的车机 APP 开发,一般都有配套的桌面小部件(Widget)开发,这里对小部件的具体实现就不介绍了,这里主要介绍一些桌面(Launcher)中的小部件(Widget)弹出窗口功能实现。 一、功能描述 在小部件上点击按钮,弹出一个有音源选择列表的弹窗,点击其他位置…...
常见的C++包管理
C包管理工具 Conan 是一款免费开源的 C/C语言的依赖项和包管理器 类似于python的anaconda Introduction — conan 2.0.4 documentationconan-io/conan: Conan - The open-source C and C package manager (github.com) CPM cmake集成的 mirrors / cpm-cmake / CPM.cmake GitC…...
基于yolov7开发构建学生课堂行为检测识别系统
yolov7也是一款非常出众的目标检测模型,在我之前的文章中也有非常详细的教程系列的文章,感兴趣的话可以自行移步阅读即可。 《基于YOLOV7的桥梁基建裂缝检测》 《YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程》 《基于YOLOv7融合…...
GPT-4 开始内测32k输入长度的版本了!你收到邀请了吗?
要说现在 GPT-4 最大的问题是什么?可能除了一时拿他没有办法的机器幻觉,就是卡死的输入长度了吧。尽管在一般的对话、搜索的场景里目前普通版本 GPT-4 的 8000 左右的上下文长度或许绰绰有余,但是在诸如内容生成、智能阅读等方面当下基础版的…...
如何用ChatGPT做新品上市推广方案策划?
该场景对应的关键词库(28个): 品牌、产品信息、新品、成分、属性、功效、人群特征、客户分析、产品定位、核心卖点、推广策略、广告、公关、线上推广、线下活动、合作伙伴、资源整合、预算、执行计划、监测、评估、微调方案、价值主张、营销策略、热点话…...
Qt之QGraphicsEffect的简单使用(含源码+注释)
文章目录 一、效果示例图1.效果演示图片3.弹窗演示图片 二.问题描述三、源码CFrame.hCFrame.cppCMainWindow.hCMainWindow.cpp 总结 一、效果示例图 1.效果演示图片 3.弹窗演示图片 二.问题描述 (因为全是简单使用,毫无技巧,直接描述问题&a…...
前端优化-css
1.css盒子模型 标准盒子模型,IE盒子模型 标准盒子模型:margin-border-padding-content IE盒子模型:margin-content(border-padding-content) 如何转换: box - sizing: border - box; // IE盒子模型 box - sizing: content - …...
第三方ipad笔哪个牌子好用?ipad触控笔推荐平价
至于选择苹果原装的电容笔,还是平替的电容笔,要看个人的需求而定,比如画图用的,可以用Apple Pencil;比如学习记笔记用的,可以用平替电容笔,目前的平替电容笔无论是品质还是性能,都非…...
windows10+detectron2完美安装教程
文章目录 前言下载detectron2安装Visual Studio 2019修改代码 前言 需要下载detectron2的github项目,安装vs2019 (强烈建议这个版本,其他的版本需要做更多地操作才能成功安装),默认其他环境没问题。 下载detectron2 链接:https…...
串口与wifi模块
经过以下学习,我们掌握: AT指令与wifi模块的测试方法:通过CH340直接测试,研究各种AT指令下wifi模块的响应信息形式。编程,使用串口中断接收wifi模块对AT指令的响应信息以及透传数据,通过判断提高指令执行的…...
上财黄烨:金融科技人才的吸引与培养
“金融科技企业在吸引人才前,应先完善人才培养机制,建立员工画像,有针对性地培训提高成员综合素质。” ——上海金融智能工程技术研究中心上海财经大学金融科技研究院秘书长&院长助理黄烨老师 01.何为数字人才? 目前大多数研…...
利用MQ事务消息实现分布式事务
MQ事务消息使用场景 消息队列中的“事务”,主要解决的是消息生产者和消息消费者的数据一致性问题。 拿我们熟悉的电商来举个例子。一般来说,用户在电商 APP 上购物时,先把商品加到购物车里,然后几件商品一起下单,最后…...
C++面向对象设计:深入理解多态与抽象类实现技巧
面向对象的多态 一、概念二、实现1. 静态多态1.1 函数重载1.2 运算符重载 2. 动态多态2.1 虚函数2.2 纯虚函数 三、虚函数1. 定义2. 实现3. 注意 四、纯虚函数1. 定义2. 作用 五、虚析构函数1. 定义2. 作用 六、 抽象类七、实现多态的注意事项1. 基类虚函数必须使用 virtual 关…...
长三角生物医药产业加速跑,飞桨螺旋桨为创新药企、医药技术伙伴装上AI大模型引擎...
生物医药是国家“十四五”规划中明确的战略性新兴产业之一。长三角地区是中国生物医药产业的排头兵,也是《“十四五”生物经济发展规划》的“生物经济先导区”之一。据《上海市生物医药产业投资指南》显示,2022 年上海市生物医药产业在 I 类国产创新药数…...
orin Ubuntu 20.04 配置 Realsense-ROS
librealsense安装 sudo apt-get install libudev-dev pkg-config libgtk-3-dev sudo apt-get install libusb-1.0-0-dev pkg-config sudo apt-get install libglfw3-dev sudo apt-get install libssl-dev sudo apt-get install ros-noetic-ddynamic-reconfigure二进制安装libr…...
MyBatis基础知识点总结
MyBatis了解 MyBatis 是什么? MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架 MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集 MyBatis 可以使用简单的XML或注解用于配置和原始映射,将接口和Java的 POJO&#x…...
校园企业车辆维修报修管理系统设计与开发
本研究课题重点主要包括了下面几大模块:在本基于.net平台的车辆系统中分为管理员和用户2个模块,主要功能包括管理员信息管理,车辆信息管理,驾驶员信息管理,事故信息管理,维修信息管理,维修点管理…...
【企业信息化】第1集 免费开源ERP: Odoo 16 CRM客户关系管理系统
文章目录 前言一、概览二、使用功能1.加快销售速度2.销售线索3.机会4.客户5.高效沟通6.报告7.集成 三、总结 前言 世界排名第一的免费开源ERP: Odoo 16 CRM客户关系管理系统。真正以客户为中心的CRM。 一、概览 获得准确预测 使用可操作数据,以做出更好的决定。 获…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...
Psychopy音频的使用
Psychopy音频的使用 本文主要解决以下问题: 指定音频引擎与设备;播放音频文件 本文所使用的环境: Python3.10 numpy2.2.6 psychopy2025.1.1 psychtoolbox3.0.19.14 一、音频配置 Psychopy文档链接为Sound - for audio playback — Psy…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
libfmt: 现代C++的格式化工具库介绍与酷炫功能
libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库,提供了高效、安全的文本格式化功能,是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全:…...
