Linux——自定义简单shell
shell
- 自定义shell
- 目标
- 普通命令和内建命令(补充)
- shell实现
- 实现原理
- 实现代码
自定义shell
目标
- 能处理普通命令
- 能处理内建命令
- 要能帮助我们理解内建命令/本地变量/环境变量这些概念
- 理解shell的运行
普通命令和内建命令(补充)
在Linux系统中,命令大致可以分为普通命令(通常指的是外部命令)和内建命令两大类。以下是关于这两类命令的详细解释:
一、普通命令(外部命令)
定义:普通命令,也被称为外部命令,是Linux系统中的实用程序部分。这些命令通常以单独的程序文件形式存在,并存储在系统的特定目录中(如/bin、/usr/bin、/sbin、/usr/sbin等)。
特点:
功能强大:由于外部命令通常是独立的程序,因此它们可以包含复杂的功能和算法。
不随系统一起加载:在系统启动时,外部命令并不会被加载到内存中。它们只在被需要时,由shell程序通过PATH环境变量查找并加载到内存中执行。
执行速度相对较慢:由于需要查找和加载程序文件,以及创建子进程来执行命令,因此外部命令的执行速度通常比内建命令慢。
示例:常见的外部命令包括ls(列出目录内容)、vi(文本编辑器)、grep(文本搜索工具)等。
二、内建命令
定义:内建命令是shell程序的一部分,这些命令被直接集成在shell的源代码中,并随着shell程序的启动而被加载到内存中。
特点:
执行速度快:由于内建命令是在shell程序内部执行的,因此它们不需要创建子进程或查找外部程序文件,从而提高了执行速度。
与shell紧密集成:内建命令与shell程序紧密集成,因此它们可以更方便地访问和操作shell的环境变量、函数等。
占用内存较少(但相对外部命令而言):虽然内建命令的执行速度更快,但它们通常会占用一定的内存空间。然而,由于这些命令是shell程序的一部分,因此它们所占用的内存空间通常是可以接受的。
示例:常见的内建命令包括cd(切换目录)、pwd(显示当前工作目录)、echo(输出字符串到标准输出)、history(显示命令历史记录)等。
三、区分方法
在Linux中,可以使用type命令来区分一个命令是内建命令还是外部命令。例如:
输入type cd,输出结果为cd is a shell builtin,表示cd是一个内建命令。
输入type ls,输出结果为ls is aliased to ‘ls --color=auto’(或类似信息),表示ls是一个外部命令(尽管这里显示了别名信息,但可以通过进一步使用type -t ls或查看其路径来确认其为外部命令)。
shell实现
实现原理
⽤下图的时间轴来表⽰事件的发⽣次序。其中时间从左向右。shell由标识为sh的⽅块代表,它随着时间的流逝从左向右移动。shell从⽤户读⼊字符串"ls"。shell建⽴⼀个新的进程,然后在那个进程中运行ls程序并等待那个进程结束。
Shell的作用流程
- 读取输入:
Shell等待用户输入命令。这可以是直接在命令行提示符下输入的命令,也可以是来自脚本文件的命令。 - 解析命令:
Shell解析用户输入的命令,包括命令本身、参数和选项。它会检查命令的语法是否正确,以及命令是否存在。 - 查找命令:
Shell判断命令是内置命令还是外部命令。如果是内置命令,则直接执行;如果是外部命令,则在PATH环境变量中查找相应的程序文件。 - 执行命令:
Shell将解析后的命令传递给操作系统内核执行。内核会读取命令,并执行相应的操作。这包括调用系统资源、管理文件、执行程序等。 - 返回输出:
当命令执行完成后,Shell会返回执行结果或错误信息给用户。这可以是命令的输出内容、状态码等。 - 环境变量:
Shell使用环境变量来存储系统配置和用户信息。这些变量在命令执行和脚本编写中起着重要作用。例如,PATH变量指定了系统在哪些目录下查找可执行文件。 - 重定向和管道:
Shell支持将命令的输入和输出重定向到文件或其他设备上。同时,它还支持管道操作,可以将一个命令的输出作为下一个命令的输入。
实现代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
using namespace std;
const int basesize = 1024;
const int argvnum = 64;
const int envnum = 64;
// 全局的命令⾏参数表char *gargv[argvnum];
int gargc = 0;
// 全局的变量int lastcode = 0;
// 我的系统的环境变量char *genv[envnum];
// 全局的当前shell⼯作路径char pwd[basesize];
char pwdenv[basesize];string GetUserName()
{string name = getenv("USER");return name.empty() ? "None" : name;
}
string GetHostName()
{string hostname = getenv("HOSTNAME");return hostname.empty() ? "None" : hostname;
}
string GetPwd()
{if(nullptr == getcwd(pwd, sizeof(pwd))) return "None";snprintf(pwdenv, sizeof(pwdenv),"PWD=%s", pwd);putenv(pwdenv); // PWD=XXXreturn pwd;//string pwd = getenv("PWD");//return pwd.empty() ? "None" : pwd;
}
string LastDir()
{string curr = GetPwd();if(curr == "/" || curr == "None") return curr;// /home/whb/XXXsize_t pos = curr.rfind("/");if(pos == std::string::npos) return curr;return curr.substr(pos+1);
}string MakeCommandLine()
{// [whb@bite-alicloud myshell]$ char command_line[basesize];snprintf(command_line, basesize, "[%s@%s %s]# ",\GetUserName().c_str(), GetHostName().c_str(),LastDir().c_str());return command_line;
}
void PrintCommandLine() // 1. 命令⾏提⽰符
{printf("%s", MakeCommandLine().c_str());fflush(stdout);
}bool GetCommandLine(char command_buffer[], int size) //2. 获取⽤⼾命令
{// 我们认为:我们要将⽤⼾输⼊的命令⾏,当做⼀个完整的字符串// "ls -a -l -n"char *result = fgets(command_buffer, size, stdin);if(!result){return false;}command_buffer[strlen(command_buffer)-1] = 0;if(strlen(command_buffer) == 0) return false;return true;
}void ParseCommandLine(char command_buffer[], int len) // 3. 分析命令
{(void)len;memset(gargv, 0, sizeof(gargv));gargc = 0;// "ls -a -l -n"const char *sep = " ";gargv[gargc++] = strtok(command_buffer, sep);// =是刻意写的while((bool)(gargv[gargc++] = strtok(nullptr, sep)));gargc--;}
void debug()
{printf("argc: %d\n", gargc);for(int i = 0; gargv[i]; i++){printf("argv[%d]: %s\n", i, gargv[i]);}
}
bool ExecuteCommand() // 4. 执⾏命令
{// 让⼦进程进⾏执⾏pid_t id = fork();if(id < 0) return false;if(id == 0){//⼦进程// 1. 执⾏命令execvpe(gargv[0], gargv, genv);// 2. 退出exit(1);}int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){if(WIFEXITED(status)){lastcode = WEXITSTATUS(status);}else{ lastcode = 100;}return true;}return false;
}void AddEnv(const char *item){int index = 0;while(genv[index]){index++;}genv[index] = (char*)malloc(strlen(item)+1);strncpy(genv[index], item, strlen(item)+1);genv[++index] = nullptr;}// shell⾃⼰执⾏命令,本质是shell调⽤⾃⼰的函数bool CheckAndExecBuiltCommand(){if(strcmp(gargv[0], "cd") == 0){// 内建命令if(gargc == 2){chdir(gargv[1]);lastcode = 0;}else{lastcode = 1;}return true;}else if(strcmp(gargv[0], "export") == 0){// export也是内建命令if(gargc == 2){AddEnv(gargv[1]);lastcode = 0;}else{lastcode = 2;}return true;}else if(strcmp(gargv[0], "env") == 0){for(int i = 0; genv[i]; i++){printf("%s\n", genv[i]);}lastcode = 0;return true;}else if(strcmp(gargv[0], "echo") == 0){if(gargc == 2){// echo $?// echo $PATH// echo helloif(gargv[1][0] == '$'){if(gargv[1][1] == '?'){printf("%d\n", lastcode);lastcode = 0;}}else{printf("%s\n", gargv[1]);lastcode = 0;}}else{lastcode = 3;}return true;}return false;
}
void InitEnv(){extern char **environ;int index = 0;while(environ[index]){genv[index] = (char*)malloc(strlen(environ[index])+1);strncpy(genv[index], environ[index], strlen(environ[index])+1);index++;}genv[index] = nullptr;}int main(){InitEnv();char command_buffer[basesize];while(true){PrintCommandLine(); // 1. 命令⾏提⽰符// command_buffer -> outputif( !GetCommandLine(command_buffer, basesize) ) // 2. 获取⽤⼾命令{continue;}//printf("%s\n", command_buffer);ParseCommandLine(command_buffer, strlen(command_buffer)); // 3. 分析命令if ( CheckAndExecBuiltCommand() ){continue;}ExecuteCommand(); // 4. 执⾏命令}return 0;}
相关文章:
Linux——自定义简单shell
shell 自定义shell目标普通命令和内建命令(补充) shell实现实现原理实现代码 自定义shell 目标 能处理普通命令能处理内建命令要能帮助我们理解内建命令/本地变量/环境变量这些概念理解shell的运行 普通命令和内建命令(补充) …...
基于matlab程序实现人脸识别
1.人脸识别流程 1.1.1基本原理 基于YCbCr颜色空间的肤色模型进行肤色分割。在YCbCr色彩空间内对肤色进行了建模发现,肤色聚类区域在Cb—Cr子平面上的投影将缩减,与中心区域显著不同。采用这种方法的图像分割已经能够较为精确的将人脸和非人脸分割开来。…...
Unity跨平台基本原理
Unity跨平台基本原理 Unity跨平台基本原理微软的.Net是什么微软做 .Net平台的目的如何实现的.Net跨语言?总结 .Net Framework.Net Framework的体系结构CLR总结 如何实现的跨平台?.Net Core.Net FrameWork 到 .Net CoreMonoMono如何实现跨平台总结如何实现…...
【前端开发】小程序无感登录验证
概述 封装的网络请求库,主要用于处理 API 请求并支持自动处理 token 过期 和 token 刷新,适用于需要身份验证的应用场景,特别是在移动端中。 主要功能 自动附加 Token 在每个请求中自动附加 Authorization 头部,使用存储的 acces…...
Flink常见面试题
1、Flink 的四大特征(基石) 2、Flink 中都有哪些 Source,哪些 Sink,哪些算子(方法) 预定义Source 基于本地集合的source(Collection-based-source) 基于文件的source(…...
spark同步mysql数据到sqlserver
使用Apache Spark将数据从MySQL同步到SQL Server是一个常见的ETL(Extract, Transform, Load)任务。这里提供一个基本的步骤指南,以及一些代码示例来帮助你完成这项工作。 ### 前提条件 1. **安装Spark**:确保你的环境中已经安装了…...
Python Web 开发:FastAPI 基本概念与应用
Python Web 开发:FastAPI 基本概念与应用 目录 ✨ 1. FastAPI 路由(定义请求路径)🚀 2. HTTP 请求方法(GET、POST、PUT、DELETE)🔑 3. 参数类型(路径参数、查询参数、请求体&#…...
Linux设置开启启动脚本
1.问题 每次启动虚拟机需要手动启动网络,不然没有enss33选项 需要启动 /mnt/hgfs/dft_shared/init_env/initaial_env.sh 文件 2.解决方案 2.1 修改/etc/rc.d/rc.local 文件 /etc/rc.d/rc.local 文件会在 Linux 系统各项服务都启动完毕之后再被运行。所以你想要…...
go并发设计模式runner模式
go并发设计模式runner模式 真正运行的程序不可能是单线程运行的,go语言中最值得骄傲的就是CSP模型了,可以说go语言是CSP模型的实现。 假设现在有一个程序需要实现,这个程序有以下要求: 程序可以在分配的时间内完成工作࿰…...
nn.RNN解析
以下是RNN的计算公式,t时刻的隐藏状态H(t)等于前一时刻隐藏状态H(t-1)乘以参数矩阵,再加t时刻的输入x(t)乘以参数矩阵,最后再通过激活函数,等到t时刻隐藏状态。 下图是输出input和初始化的隐藏状态,当参数batch_first True时候&…...
How to monitor Spring Boot apps with the AppDynamics Java Agent
本文介绍如何使用 AppDynamics Java 代理监视 Azure Spring Apps 中的 Spring Boot 应用程序。 使用 AppDynamics Java 代理可以: 监视应用程序使用环境变量配置 AppDynamics Java 代理 在 AppDynamics 仪表板中检查所有监视数据 How to monitor Spring Boot app…...
Linux学习笔记12 systemd的其他命令
前文已经介绍了systemd在系统初始化中起到的作用和服务的管理和配置。这里补充一下systemd的其他工具和系统进程的管理 前文 Linux学习笔记10 系统启动初始化,服务和进程管理(上)-CSDN博客 Linux学习笔记11 系统启动初始化,服务…...
NGO-CNN-BiGRU-Attention北方苍鹰算法优化卷积双向门控循环单元时间序列预测,含优化前后对比
NGO-CNN-BiGRU-Attention北方苍鹰算法优化卷积双向门控循环单元时间序列预测,含优化前后对比 目录 NGO-CNN-BiGRU-Attention北方苍鹰算法优化卷积双向门控循环单元时间序列预测,含优化前后对比预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介…...
【分布式】分布式缓存
一、什么是分布式缓存 分布式缓存是一种将缓存数据存储在多个节点上的缓存方案。它通过将数据分散存储在多个节点的内存中,以提高系统的读取性能、降低数据库压力和提高系统可扩展性。 二、分布式缓存的优点 优点明细提高性能:分布式缓存可以将数据缓…...
深度学习中的迁移学习:应用与实践
引言 在深度学习领域,迁移学习(Transfer Learning)是一个非常强大且日益流行的概念,它通过将从一个任务中学到的知识应用于另一个任务,能够显著加快模型训练速度并提高其泛化能力。迁移学习在许多实际应用中都得到了广…...
28.UE5实现对话系统
目录 1.对话结构的设计(重点) 2.NPC对话接口的实现 2.1创建类型为pawn的蓝图 2.2创建对话接口 3.对话组件的创建 4.对话的UI设计 4.1UI_对话内容 4.2UI_对话选项 4.3UI_对话选项框 5.对话组件的逻辑实现 通过组件蓝图,也就是下图中的…...
Redis中的分布式锁(步步为营)
分布式锁 概述 分布式锁指的是,所有服务中的所有线程都去获取同一把锁,但只有一个线程可以成功的获得锁,其他没有获得锁的线程必须全部等待,直到持有锁的线程释放锁。 分布式锁是可以跨越多个实例,多个进程的锁 分布…...
CentOS 7安装mysql+JDK+Tomcat完成流程
一.安装mysql 即使是新的linux服务器,也要先验证是否有mysql已经安装,如果有进行卸载原版本,一定要确认是否mysql已不再使用 原安装情况(直接执行命令即可) whereis mysql rpm -qa | grep -i mysql rpm -e perl-DBD-M…...
C++笔记之不同框架中事件循环的核心函数:io_run()、ros_spin()、app_exec()
C笔记之不同框架中事件循环的核心函数:io_run()、ros_spin()、app_exec() code review! 参考笔记 1.qt-C笔记之使用QtConcurrent异步地执行槽函数中的内容,使其不阻塞主界面 2.qt-C笔记之QThread使用 3.qt-C笔记之多线程架构模式:事件信号监…...
C++异常处理
目录 一、异常的概念 二、异常的使用 (1)异常的抛出和捕获 (2)异常的重新抛出 (3)异常安全 (4)异常规范 三、自定义异常体系 四、c标注异常体系 五、异常的优缺点 在之前我们…...
【数据结构】哈希 ---万字详解
unordered系列关联式容器 在C98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到log_2 N,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好 的查询是,…...
4399大数据面试题及参考答案(数据分析和数据开发)
对数据分析的理解 数据分析是一个从数据中提取有价值信息以支持决策的过程。它涵盖了数据收集、清洗、转换、建模和可视化等多个环节。 首先,数据收集是基础。这包括从各种数据源获取数据,例如数据库、文件系统、网络接口等。这些数据源可以是结构化的数据,如关系型数据库中…...
快速理解倒排索引在ElasticSearch中的作用
一.基础概念 定义: 倒排索引是一种数据结构,用来加速文本数据的搜索和检索,和传统的索引方式不同,倒排索引会被每个词汇项与包含该词汇项的文档关联起来,从而去实现快速的全文检索。 举例: 在传统的全文…...
C++趣味编程玩转物联网:基于树莓派Pico控制无源蜂鸣器-实现音符与旋律的结合
无源蜂鸣器是一种多功能的声音输出设备,与有源蜂鸣器相比,它能够通过不同频率的方波生成丰富多样的音调。本项目使用树莓派Pico开发板,通过编程控制无源蜂鸣器播放经典旋律《归来有风》。本文将详细介绍项目实现中的硬件连接、C++代码解析,以及无源蜂鸣器的工作原理。 一、…...
《RuoYi基于SpringBoot+Vue前后端分离的Java快速开发框架学习》系列博客_Part4_三模态融合
系列博客目录 文章目录 系列博客目录目标Step1:之前工作形成子组件Step2:弥补缺失的文本子组件,同时举例如何子组件向父组件传数据Step3:后端代码需要根据上传的文件传给python服务器Step4:python服务器进行分析 目标 实现三模态融合,将文本、图片、音频…...
springboot365高校疫情防控web系统(论文+源码)_kaic
毕 业 设 计(论 文) 题目:高校疫情防控的设计与实现 摘 要 互联网发展至今,无论是其理论还是技术都已经成熟,而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播,搭配信息管理工具可以很好地为…...
STM32 USART串口数据包
单片机学习! 目录 前言 一、数据包 二、HEX数据包 三、文本数据包 四、HEX数据包和文本数据包优缺点 4.1 HEX数据包 4.2 文本数据包 五、HEX数据包接收 六、文本数据包接收 总结 前言 本文介绍了串口数据包收发的思路和流程。 一、数据包 数据包的作用是把一个个单独…...
【LC】3232. 判断是否可以赢得数字游戏
题目描述: 给你一个 正整数 数组 nums。 Alice 和 Bob 正在玩游戏。在游戏中,Alice 可以从 nums 中选择所有个位数 或 所有两位数,剩余的数字归 Bob 所有。如果 Alice 所选数字之和 严格大于 Bob 的数字之和,则 Alice 获胜。如果…...
Linux基础学习--vi与vim
0.绪论 前面的内容基本学完了相关命令行,后面进行shell与shell script的学习。第一部分就是编辑器的学习,之前有写过vi/vim编辑器,但是我看了一下鸟哥这个非常详细,还是打算重头学习一下。 1.vi/vim的使用 一般命令模式(command…...
JavaScript 高级教程:异步编程、面向对象与性能优化
在前两篇教程中,我们学习了 JavaScript 的基础和进阶内容。这篇文章将带领你进入更深层次,学习 JavaScript 的异步编程模型、面向对象编程(OOP),以及性能优化的技巧。这些内容对于构建复杂、流畅的前端应用至关重要。 …...
哪家能建设网站/公司网站推广技巧
大脑血管丰富,充分的血液供应才能保证大脑正常发挥其功能。而一旦血管出现堵塞,就意味着与时间的赛跑开始了。案例就在11月中旬,北京一连两天的降雨,让气温骤降至寒冬。宣武医院急诊神经内科诊室,接连不断地送进来了脑…...
企业网站seo关键词优化排名思路/手机网站排名优化软件
1 [System.Serializable] 使自定义的类能进行序列化,即当做一个public成员的时候可以在Inspector显示。 [SerializeField] 在Inspector版面中显示非public属性,并且序列化;若写在public前面,等于没写 2 [NonSerialized]在Insp…...
搜索引擎网站推广法怎么做/百度网站推广费用
混合云应基于商业化的现成的产品,特别是使用X64架构。ARM64也是一个可能的市场竞争者,但它是新的,仍然还需要一些在管理程序环境的证明。至于其他的方法,使用复杂的UNIX系统与一种基于大型主机的混合云的想法是非常矛盾的。 基于上…...
温州专业网站建设/人员优化是什么意思
枚举进程 每一个应用程序实例在运行起来后都会在当前系统下产生一个进程,大多数应用程序均拥有可视界面,用户可以通过标题栏上的关闭按钮关闭程序。但是也有为数不少的在后台运行的程序是没有可视界面的,对于这类应用程序用户只能通过CTRLALT…...
wordpress ucenter/电商seo是什么
组件可以有自己的data,并且data必须是一个function。 在下面这个例子中,data 返回了一个在外部定义的对象。并且这个组件在页面中使用了3次,点击1时出现了如下情况:data中的count属性影响到了所有实例。 <html><head&…...
郑州网络推广大包/网站如何做seo排名
JDK线程池:Executors.newFixedThreadPool , Executors.newSingleThreadExecutor,由一个ThreadFactory来创建新的线程,默认情况下为Executors.defaultThreadFactory(),ThreadFactory接口: public interface ThreadFacto…...