Linux 简易shell编写
shell
shell是壳,外壳的意思,一般我们使用linux系统有用图形化界面的也有使用命令行界面的,这两个都是一种shell,以命令行为例:

如图
这个就是我这里的命令行格式,在$符后面写的就是执行的指令,ls指令在linux中也是一个可执行程序,这个程序作用就是显示出当前路径下的文件。可是能执行命令程序就代表我们找到这个文件并对其进程了执行的操作,而我们只是在$符后面写下了ls而已-l是一个选项是传递给ls这个程序的一个参数。能完成找到ls程序并执行它并将参数传递给这个程序的工作就是shell做的事。所以shell也是为了完成某一目的而被编写出来的一个程序,这个程序会在我们打开软件的时候自动执行。我这里用的是XSHELL5也是这个软件的功能之一。
在shell环境下编写shell,我们为什么能在shell环境下编写使用shell呢,这就是shell进程设计的工作模式,当我们使用命令行执行一个程序的时候shell程序会被挂起执行启动的程序,直到我们启动的程序结束shell程序才会接管执行,这个就是父子进程的关系,shell进程作为父进程,命令行输入的程序作为子进程,当执行子进程时父进程对子进程进行等待,子进程结束后父进程在等待后继续执行,这就是shell的工作模式。
shell命令行
![]()
我们能看到当进入程序的时候一开始就会看到这样一个命令行提示字符串,里面有我现在用户名,有主机名,有当前路径名字。
这就是shell的第一个模块,进入shell我们需要先有一个提示的字符串

这个是打印命令行提示的代码,先定义一个字符数组用于存放完整的提示,定义了三个字符串指针用于接收对应的数据



这里对应着三个所调用的函数分别是获取用户名,获取主机名,获取路径。三个函数用的是同一个库函数getenv作用是根据传递的参数在环境变量中找寻对应的值。返回字符串首位的地址,然后将这个字符串返回。
由于这里通过环境变量返回的路径是绝对路径所以这里我做了一点小修改

只截取了最后的一个路径,不然整个命令行提示会随着层数加深越来越长。

然后我们只要按照一定的格式将其写入到一开始定义的字符数组中然后打印到屏幕上因为我们后续是直接在命令行提示后面直接写入指令的所以这里不需要要换行,但是系统缓冲区的刷新是按照行刷新的所以这里需要手动刷新标准输出的缓冲区。
获取指令
指令我们都是从命令行中获取,而命令行中其实就是打印了标准输入中的数据,所以实际上是从标准输入中获取的。
因为每次输入都是新的指令,旧的指令就放弃掉,有很多外壳程序会有存储上几条输入的指令所以我们是可以malloc一个缓冲区来存放需要存储的指令的,但是我们这里就简单实现就直接对之前的指令不做处理默认丢弃
![]()
我这里在main函数中定义了一个字符数组存放指令这个数组过了生命周期就自动释放每次获取的时候就从新创建,然后将这个数组作为输出型参数传递给获取指令的函数。

这个数组的大小是需要传递的,因为数组传参是传的指针,数组的大小只能在定义的作用域内计算。然后使用fgets函数从标准输入中获取指令到缓冲区中。
标准输入中是不会包含字符串结束符的,"\0"作为字符串的结束标志是C语言中规定的,这里需要自己添加进去,这里返回获取到的字符串的长度。这里不用scanf的原因是scanf遇到空格会当作输入结束就无法获取一整行的指令。

问我这里判断输入用<=0表示程序出错了直接停止程序,因为无论在命令行中输入什么都不可能出现<=0的情况就算只输入了空格回车那也有一个空格符或者回车。
指令分割
指令接收是一整行的,但是里面包含了程序名或路径加程序名还有一些选项,就需要将选项和程序分割开来。
这里将参数分割开来后需要存储,这里定义一个全局的字符指针数组存放。![]()

这里使用strtok函数分割,这个函数有一些些奇怪,第一次分割需要把其实地址和分割的符号传进去这里SEP是一个宏
是一个空格,这个分隔符是一个字符串不是字符。然后继续分割就不需要地址了,给他一个NULL会自动在之前的位置继续分割,他会返回分割后的首字符指针,第一个分割的还在原来的缓冲区中,后续分割的会存放在静态区中,这是strtok的性质,所以分割后最好不要继续使用main函数中的缓冲区的用户指令了,我们会将分割后指令和选项的字符串首地址放在全局的字符指针数组中,后续就用这个数组就可以了。
指令执行
分割完指令我能就从全局的字符指针数组中获取单个的指令和选项了,那就可以执行这个指令了,这个指令也是一个程序,我们也没必要将所有程序功能都集中在shell程序中,而且都有现成的还去实现这么的程序功能这样耦合度过高不好,效率也不行,所以我们需要在创建一个执行指令的进程。这里我们使用子进程加程序替换的方式来实现。

子进程的创建替换,父进程的等待都打包给了函数,就不需要主进程这么麻烦了,直接调用就可以了。进程创建替换详细在进程篇中,就说一下execvp这个函数,exec函数家族有好几个函数,主要是参数不同能适应各种场景,这里使用vp是可以直接匹配系统的环境变量就不需要我们自己自定义环境变量了传参了,v是数组的意思我们能以数组的形式传递选项的参数,我们定义的参数列表就是一个字符指针数组所以用这个函数最方便了,第一个参数传入我们需要执行的程序名或路径加程序名,然后将整个参数列表都传入给第二个参数就完成进程替换,若失败返回-1,设置错误码,我们子进程直接退出返回错误码给父进程,若是成功就变成了第一个参数的程序了就不用管了。
父进程就只需要等待子进程完成就可以了,这里我们只对进程替换失败做一些工作,获取错误码并打印。
内建指令
完成上述步骤最简单的外壳程序就已经完成了,能获得用户输入的信息并创建子进程执行对应的程序。只是还有一些小瑕疵,有一些指令例如cd指令要变更当前路径,但这里是创建子进程执行这个程序所以父进程的路径并不会更改,而是子进程的路径被更改了,子进程路径被修改是没有意义的,我的都是从shell程序作为基点来使用linux系统的,所以这条指令需要由父进程自己执行,类似这种的指令叫做内建指令,不能由子进程执行。

在调用子执行函数前先检查是否为内建命令若是就单独执行这个命令,不然子进程执行。

cd指令比较复杂因为不止要执行还需要对环境变量进行更改,还要对命令行提示进行修改所以单独写了一个函数执行,这里先拿到指令的第二个参数即数组下标为一的参数,若这个参数为NULL就说明用户只写了cd,这时我们调用一个函数获取环境变量中的home路径

这个正常的系统都是有的,没有的那些要不自己更改了,要不系统就有些错误了。不过我们还是将没有的也考虑了一下返回一个根目录。
然后使用系统调用函数更改当前路径,这里参数是用的绝对路径。
更改完路径需要对环境变量中的路径进行修改,先定义一个存放新路径的字符数组,使用getcwd这是个c库函数,获取当前的最新路径,然后以标准格式存放,这里是用了一个全局的cwd字符数组存放
![]()
这时系统env里面的标准格式,getcwd是只获得路径,前面的PWD=需要手动添加上去,然后使用putenv函数更新当前工作路径完成cd全部步骤。
相关文章:
Linux 简易shell编写
shell shell是壳,外壳的意思,一般我们使用linux系统有用图形化界面的也有使用命令行界面的,这两个都是一种shell,以命令行为例: 如图这个就是我这里的命令行格式,在$符后面写的就是执行的指令,…...
POLYGON Nature - Low Poly 3D Art by Synty 树木植物
一个低多边形资源包,包含可以添加到现有多边形风格游戏中的树木、植物、地形、岩石、道具和特效 FX 资源。 为 POLYGON 系列提供混合样式树这一新增功能。弥合 POLYGON 与更传统的层级资源之间的差距。还提供了一组经典的 POLYGON 风格的树木和植被以满足你的需求。 该包还附带…...
了解什么是瞪羚企业
瞪羚企业”是指以科技创新或商业模式创新为支撑,进入高成长期的中小企业。识别范围主要是符合国家和省战略性新兴产业发展方向的产业领域,涵盖新兴产业、新一代信息技术(包括大数据、物联网和云计算、高端软件、互联网)、生物健康…...
寻找两个正序数的中位数(C)
最近面试,发现要手撕算法加上机试,被完败,索性给自己立一个目标,一周训练2次。 第一题。 给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 …...
YOLOv10涨点改进:IoU优化 | Unified-loU,用于高品质目标检测的统一loU ,2024年8月最新IoU
💡💡💡现有IoU问题点:IoU (Intersection over Union)作为模型训练的关键,极大地显示了当前预测框与Ground Truth框之间的差异。后续研究者不断在IoU中加入更多的考虑因素,如中心距离、纵横比等。然而,仅仅提炼几何差异是有上限的;而且新的对价指数与借据本身存在潜在…...
Spring Boot 实现动态配置导出,同时支持公式和动态下拉框渲染和性能优化案例示范
在业务系统中,数据导出是一个非常常见且重要的功能,本文将详细介绍如何在 Spring Boot 中实现这一功能,并结合 MySQL 数据库、MyBatis 作为数据访问层,EasyExcel 作为导出工具,展示如何在电商交易系统中搭建灵活、可扩…...
一网打尽 运维必封的50个高危端口清单,零基础入门到精通,收藏这一篇就够了
文件传输相关端口: • TCP 20、21:FTP 服务(文件传输协议)端口,FTP 传输数据时未加密,容易受到攻击,如匿名上传下载、爆破、嗅探、远程执行等攻击,可能导致敏感文件泄露。 • TCP …...
方法 WebDriverWait
定义: WebDriverWait是Selenium WebDriver提供的一个工具类,它允许你设置等待条件,直到这个条件成立,才继续执行代码。这对于处理网页上的异步加载元素特别有用,比如等待某个元素变得可见、可点击等。 from se…...
LOESS(Locally Estimated Scatterplot Smoothing)
文章目录 LOESS 原理详解:LOESS 的优点:LOESS 的缺点:Python 实现代码:代码说明: LOESS(Locally Estimated Scatterplot Smoothing),即局部加权回归,是一种非参数回归方法…...
每天学习一个技术栈 ——【Django Channels】篇(1)
在当今快速发展的技术领域,掌握多种技术栈已经成为开发者提升竞争力的关键。随着实时应用需求的不断增加,如何高效地处理并发请求和实时通信变得尤为重要。在众多解决方案中,Django Channels作为Django框架的强大扩展,能够轻松实现…...
js设计模式-工厂模式 单例模式 观察者模式 发布订阅模式 原型模式 代理模式 迭代器模式
1 工厂模式 // 工厂模式: 调用函数返回对象function factory(name, age){return {name: name,age: age} }const person1 factory(Tom, 18); // 类似的库使用工厂函数的有: jQuery, React.createElement,axios.create,vue.createApp等 2 单例模式 // 单例模式:单…...
关于Java中的List<User>如何进行深拷贝
联调中发现了一个很初级,但有容易被忽略的拷贝问题: 错误方式:List<User> us new ArrayList<>(); // name "张三"List<User> us1 new ArrayList<>(us);for (User u : us) {...u.setName("douzi&q…...
2025 年 IT 前景:机遇与挑战并存,人工智能和云计算成重点
云计算de小白 投资人工智能:平衡潜力与实用性 到 2025 年,人工智能将成为 IT 支出的重要驱动力,尤其是在生成式人工智能领域。人工智能的前景在于它有可能彻底改变业务流程、增强决策能力并开辟新的收入来源。然而,现实情况更加微…...
Cortex-A7和Cortex-M7架构处理器取中断向量全流程分析
0 参考资料 Cortex M3权威指南(中文).pdf ARM Cortex-A(armV7)编程手册V4.0.pdf1 Cortex-A7和Cortex-M7处理器架构取中断向量全流程分析 1.1 什么是中断向量? 中断向量就是中断服务函数入口地址,例如我们发生了EXTI0中断,就需要执行EXT0中…...
MODELS 2024震撼续章:科技与可持续性的未来交响曲
MODELS 2024国际会议正如火如荼地进行着,每一天都充满了新的发现与启迪,每一场分享都是对技术前沿的一次深刻探索,更是对现实世界可持续性挑战的一次积极回应。现在让我们继续这场科技盛宴,看看小编为您精选几场的学术分享吧~ 会议…...
CICD 持续集成与持续交付
一 、CICD是什么 CI/CD 是指持续集成(Continuous Integration)和持续部署(Continuous Deployment)或持续交付(Continuous Delivery) 1.1 持续集成(Continuous Integration) 持续集…...
“数据面”(Data Plane)是指负责实际数据处理和转发的部分
在计算机网络和服务架构中,“数据面”(Data Plane)是指负责实际数据处理和转发的部分。数据面负责执行具体的网络通信任务,如接收、处理和转发数据包。与数据面对应的是“控制面”(Control Plane)ÿ…...
面试题:MySQL你用过WITH吗?领免费激活码
感谢Java面试教程的Java多线程文章,点击查看>原文 Java面试教程,发mmm116可获取IDEA-jihuoma 在MySQL中,WITH子句用于定义临时表或视图,也称为公共表表达式(CTE)。它允许你在一个查询中定义一个临时结果…...
consul 介绍与使用,以及spring boot 项目的集成
目录 前言一、Consul 介绍二、Consul 的使用三、Spring Boot 项目集成 Consul总结前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。 提示:以下是…...
Linux常用命令shell常用知识 。。。。面试被虐之后,吐血整理。。。。
Linux三剑客&常用命令&shell常识 Linux三剑客grep - print lines matching a patternsed - stream editor for filtering and transforming textawkman awk Linux常用命令dd命令ssh命令tar命令curl命令top命令tr命令xargs命令sort命令du/df/free命令 shell 知识functio…...
OpenLayers 可视化之热力图
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 热力图(Heatmap)又叫热点图,是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
