当前位置: 首页 > article >正文

Linux实现简易版Shell的代码详解

一、程序流程分析我们日常使用Bash时通过输入命令执行相应的操作比如那么Bash是如何进行工作的呢观察一下就会发现首先Bash会打印命令行提示符包括当前用户、主机名以及路径。之后会等待我们输入相关命令然后根据命令执行相应程序。程序执行结束后就会再次打印命令行提示符等待我们再次输入指令…很明显是一个死循环。总结一下Bash的大体工作流程是1.打印命令行提示符包括当前用户名、主机名、路径2.获取用户输入的命令行3.解析命令行4.执行命令5.继续打印命令行提示符…注意当Bash执行非内建命令时会创建一个子进程由子进程完成相应的工作Bash自己等待子进程工作结束。而对于内建命令如cdecho需要Bash自己执行任务。二、代码实现接下来我们开始按照上述工作流程一步步实现我们的简易Shell。1. 打印命令行提示符CentOS的命令行提示符主要包含三个内容当前用户名、主机名和当前所在路径。之前学习Linux环境变量时我们了解到环境变量USER、HOSTNAME、PWD中存储着这些内容。所以我们使用getenv函数获取环境变量相应的值。代码实现12345678910111213141516171819202122232425262728#include iostream#include cstdio#include cstring#include cstdlib#include unistd.h#include sys/types.h#include sys/wait.h//获取当前用户名constchar* GetUserName(){constchar* name getenv(USER);returnname nullptr ?none: name;}//获取当前主机名constchar* GetHostName(){constchar* name getenv(HOSTNAME);returnname nullptr ?none: name;}//获取当前工作路径constchar* GetPwd(){constchar* pwd getenv(PWD);returnpwd nullptr ?none: pwd;}接下来调用这些函数形成一串命令行提示符并打印12345678//创建并打印命令行提示符voidPrintCommandPrompt(){charCommandPrompt[1024];//这里为了区分Bash用大括号snprintf(CommandPrompt,sizeofCommandPrompt,{%s%s %s} , GetUserName(), GetHostName(), GetPwd());std::cout CommandPrompt std::flush;// 打印并刷新缓冲区}注意这里打印结束后由于没有换行所以缓冲区可能不会刷新导致命令行提示符没有出现在屏幕上。因此这里我们需要主动刷新缓冲区。进行测试12345intmain(){PrintCommandPrompt();return0;}运行结果可以看到用户名和主机名都正常打印但在我们写的shell中当前所在路径是绝对路径太过冗长所以可以对生成的路径进行一些处理1234567891011121314//创建并打印命令行提示符voidPrintCommandPrompt(){charCommandPrompt[1024];//处理当前工作路径std::string pwd GetPwd();if(pwd !/)// 如果是根目录则直接输出pwd pwd.substr(pwd.rfind(/) 1);// 查找最后一个/并从下一个位置开始分割//这里为了区分Bash用大括号snprintf(CommandPrompt,sizeofCommandPrompt,{%s%s %s} , GetUserName(), GetHostName(), pwd.c_str());std::cout CommandPrompt std::flush;// 打印并刷新缓冲区}运行结果2. 获取用户输入的命令行在Bash输入命令时往往会带上一些选项并用空格隔开。因此使用scanf或cin读入时会以空格作为分隔符达不到想要的效果。这里我们选择用fgets进行读取。另外主函数当中命令的全部处理应该放在一个死循环当中这样才能完成用户多次派发的任务。1234567891011121314151617181920212223242526272829303132333435//获取用户输入的命令行boolGetCommandLine(char* command,intsize){//从键盘读取命令if(fgets(command, size, stdin) nullptr){returnfalse;}//注意清理末尾的\nif(strlen(command) 0)returnfalse;command[strlen(command) - 1] \0;returntrue;}intmain(){while(true){//打印命令行提示符PrintCommandPrompt();//读入命令charcommand[1024];if(!GetCommandLine(command,sizeofcommand))// 若读取错误就continue重新读取{std::cout 读取错误 std::endl;continue;}//打印输入的命令行std::cout command std::endl;}return0;}注意使用fgets从键盘读取字符串时会附带末尾’\n’需要进行处理。运行测试可以看到程序成功读入了我们的命令包括空格并且将命令回显出来。3. 命令行解析命令行解析的过程中需要将用户读入的命令行进行分割提取出要执行的程序名以及选项。这里我们创建两个全局变量g_argc和g_argv分别存储解析到的命令行参数以及参数个数方便后续指令的执行。注这里的命令行分割操作由strtok函数完成。代码实现12345678910111213141516171819202122232425262728293031323334353637383940414243444546//全局变量存储命令行参数及其个数intg_argc 0;char* g_argv[128];//命令行解析boolCommandParse(char* command){g_argc 0;for(char* p strtok(command, ); p ! nullptr; p strtok(nullptr, )){g_argv[g_argc] p;}returng_argc 0 ?false:true;}intmain(){while(true){//打印命令行提示符PrintCommandPrompt();//读入命令charcommand[1024];if(!GetCommandLine(command,sizeofcommand))// 若读取错误就continue重新读取{std::cout 输入错误 std::endl;continue;}// //打印输入的命令行// std::cout command std::endl;//命令行解析if(!CommandParse(command)){std::cout 命令行解析失败 std::endl;continue;}//打印解析结果for(inti 0; i g_argc; i){std::cout g_argv[i] std::endl;}}return0;}测试结果程序成功地按照空格将我们输入的命令行参数提取了出来。接下来根据提取到的参数就可以执行相关指令了。4. 执行命令对于非内建命令Bash会创建子进程并让子进程执行而对于内建命令则是由Bash自己执行。因此执行命令之前需要先判断该命令是否是内建命令然后进行相应的操作。为什么会有内建命令效率执行内建命令通常比执行外部命令更快因为避免了创建新进程的开销。Shell 功能许多内建命令直接操作 Shell 的内部状态例如改变当前工作目录 (cd)、设置环境变量 (export)、控制 Shell 行为等这些功能如果作为外部命令实现会更加复杂或不可能。基本操作一些非常基础和常用的操作需要作为内建命令提供以确保 Shell 的基本功能可用内建命令的处理在Bash当中可以使用type命令判断一个命令是否是内建命令例如当然除了cd和echo命令还有printf、help等内建命令。本次实现中为了能够让大家深刻理解shell运行原理同时降低实现难度博主就只针对cd和echo这两个内建命令进行简易实现。内建命令的检查和处理12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758//内建命令处理boolCheckAndExecBuiltin(){//取出命令行参数表的首元素判断是否为内建命令如果是则直接执行std::string str g_argv[0];if(str cd){Cd();returntrue;}elseif(str echo){Echo();returntrue;}//else if(...)returnfalse;// 不是内建命令直接返回}intmain(){while(true){//打印命令行提示符PrintCommandPrompt();//读入命令charcommand[1024];if(!GetCommandLine(command,sizeofcommand))// 若读取错误就continue重新读取{std::cout 输入错误 std::endl;continue;}// //打印输入的命令行// std::cout command std::endl;//命令行解析if(!CommandParse(command)){std::cout 命令行解析失败 std::endl;continue;}// //打印解析结果// for(int i 0; i g_argc; i)// {// std::cout g_argv[i] std::endl;// }//内建命令的处理if(CheckAndExecBuiltin()){continue;// 是内建命令执行完毕就回去重新打印提示符}//不是内建命令由子进程处理}return0;}cd的简易实现cd的功能是改变当前工作路径。如果创建子进程其只能修改它自己的工作路径而无法修改Bash的工作路径。因此cd操作需要Bash亲自完成。我们获取到命令行参数后可以通过调用chdir函数实现1234567891011121314voidCd(){std::string dst;if(g_argc 1 || g_argv[1] std::string(~))// 处理进入家目录的情况{dst GetHome();if(dst )return;}else{dst g_argv[1];}chdir(dst.c_str());}测试结果可以看到使用cd后当前路径貌似并没有发生改变。为什么呢实际上chdir确实起到了效果但是我们的命令行提示符中当前工作路径是从环境变量中获取的环境变量中的PWD并没有发生改变。因此修改当前工作路径之后要顺带着修改环境变量PWD的值。其次当前工作路径改变后修改环境变量之前要获取到当前工作路径就需要使用getcwd函数。进行键值处理后使用putenv修改环境变量。

相关文章:

Linux实现简易版Shell的代码详解

一、程序流程分析我们日常使用Bash时,通过输入命令执行相应的操作,比如:那么,Bash是如何进行工作的呢?观察一下,就会发现,首先Bash会打印命令行提示符,包括当前用户、主机名以及路径…...

SenseNova-SI-1.5:8B参数大模型空间智能新突破

SenseNova-SI-1.5:8B参数大模型空间智能新突破 【免费下载链接】SenseNova-SI-1.5-InternVL3-8B 项目地址: https://ai.gitcode.com/SenseNova/SenseNova-SI-1.5-InternVL3-8B 导语 SenseNova-SI-1.5-InternVL3-8B大模型正式发布,以8B轻量化参数…...

OpenClaw故障排查大全:Gemma-3-12b-it接口连接失败解决方案

OpenClaw故障排查大全:Gemma-3-12b-it接口连接失败解决方案 1. 问题背景与典型场景 上周我在本地部署Gemma-3-12b-it模型时,遭遇了OpenClaw连接失败的经典问题。明明模型服务已经启动,OpenClaw网关也显示运行正常,但每次发起请求…...

节能模式!OpenClaw优化Qwen3-4B模型夜间任务功耗

节能模式!OpenClaw优化Qwen3-4B模型夜间任务功耗 1. 为什么需要关注OpenClaw的能耗问题 去年夏天,我的MacBook Pro在运行OpenClaw执行夜间数据整理任务时,风扇狂转的声音把我从睡梦中吵醒。摸到发烫的机身时,我突然意识到——这…...

dumper.js性能优化:大型对象检查的10个实用技巧

dumper.js性能优化:大型对象检查的10个实用技巧 【免费下载链接】dumper.js A better and pretty variable inspector for your Node.js applications 项目地址: https://gitcode.com/gh_mirrors/du/dumper.js dumper.js是一款为Node.js应用打造的变量检查工…...

TIPI内存管理揭秘:10个技巧优化PHP性能与防止内存泄漏

TIPI内存管理揭秘:10个技巧优化PHP性能与防止内存泄漏 【免费下载链接】tipi Thinking In PHP Internals, An open book on PHP Internals 项目地址: https://gitcode.com/gh_mirrors/ti/tipi PHP作为世界上最流行的服务器端脚本语言之一,其内存管…...

Pixel Language Portal保姆级教程:Hunyuan-MT-7B模型支持动态温度调节(per-language temperature)

Pixel Language Portal保姆级教程:Hunyuan-MT-7B模型支持动态温度调节(per-language temperature) 1. 认识你的像素翻译伙伴 Pixel Language Portal(像素语言跨维传送门)是一款基于腾讯Hunyuan-MT-7B大模型构建的创新…...

二次元助手:OpenClaw+Phi-3-mini-128k-instruct自动整理动漫资源库

二次元助手:OpenClawPhi-3-mini-128k-instruct自动整理动漫资源库 1. 为什么需要自动化整理动漫资源库 作为一个资深动漫爱好者,我的下载文件夹常年处于混沌状态。新番、老番、剧场版混杂在一起,文件名千奇百怪——有的带字幕组前缀&#x…...

OpenClaw飞书机器人配置:基于gemma-3-12b-it的对话自动化

OpenClaw飞书机器人配置:基于gemma-3-12b-it的对话自动化 1. 为什么选择OpenClaw飞书本地模型组合 去年我接手了一个需要频繁处理文档和数据的项目,每天要花3小时在飞书群聊和本地文件之间来回切换。尝试过各种自动化工具后,最终选择了Open…...

2026年上海,这些行业内正规的GEO企业,你知道有哪些吗?

在上海这座充满活力与创新的城市,GEO(地理信息)行业发展得如火如荼。随着科技的不断进步,地理信息技术在各个领域的应用越来越广泛,众多正规的GEO企业也如雨后春笋般涌现。今天,就让我们一起来了解一下上海…...

代码随想录算法第五十三天| KamaCoder110字符串迁移、KamaCoder105有向图的完全联通、KamaCoder106海岸线的计算

KamaCoder 110 字符串迁移 题目链接:110.字符串迁移 文档讲解:代码随想录 视频讲解: 字符串迁移 思路与感想:直接卡在读懂题意这一关了,我还纳闷输出结果怎么跟自己理解的差那么多。卡哥讲完之后才明白题目的推导过程…...

使用PHP Imagick扩展将PDF转换为图片功能的完整方案

引言在开发中,经常需要将 PDF 文档转换为图片格式,以便于在线预览、生成缩略图或进行其他图像处理操作。PHP 的 Imagick 扩展提供了强大的图像处理能力,可以轻松实现这一需求。本文将介绍如何使用 Imagick 扩展创建一个高效的 PDF 转图片工具…...

PHP中HTML标签过滤的5种有效方法

什么是XSS攻击? XSS(Cross-Site Scripting)攻击是指攻击者在网页中插入恶意脚本,当其他用户浏览该页面时,恶意脚本会被执行,从而盗取用户信息、会话令牌或进行其他恶意操作。 方法一:htmlspeci…...

PHP解决跨域请求问题的两种实用方法详解

引言在Web开发中,跨域资源共享(CORS)是一个常见的问题,当前端页面与后端API不在同一个域名下时,浏览器的同源策略会阻止跨域请求。本文将介绍两种在PHP中解决跨域请求问题的实用方法。什么是跨域问题?跨域指…...

PHP利用Opcache实现保护源码的示例详解

不用 IonCube(或类似的)。不知道这是啥的话,就是加密 PHP 代码但还能运行的工具。问题是太贵了。性能要好,PHP 原生支持。后来想到,PHP 有个"opcache"功能,能把源码编译成操作码(机器…...

PHP serialize进行序列化工作的完全指南

如果你和我一样,第一次在 PHP 中看到序列化字符串时会觉得很困惑。我当时在做一个 Laravel 项目,想搞清楚将任务推送到队列时到底发生了什么。我发现一些数据被序列化了,但不知道为什么以及怎么工作的。不过在我花时间研究序列化后&#xff0…...

WuliArt Qwen-Image Turbo效果对比:FP16黑图频发 vs BF16稳定出图实测

WuliArt Qwen-Image Turbo效果对比:FP16黑图频发 vs BF16稳定出图实测 1. 引言:从“黑图”困扰到稳定出图 如果你用过一些本地部署的文生图模型,可能遇到过这样的糟心事儿:满怀期待地输入一段描述,点击生成&#xff…...

Qwen3.5-9B快速上手:3步启动WebUI(supervisorctl restart)超详细步骤

Qwen3.5-9B快速上手:3步启动WebUI(supervisorctl restart)超详细步骤 1. 开篇介绍 Qwen3.5-9B是一款拥有90亿参数的开源大语言模型,具备强大的逻辑推理、代码生成和多轮对话能力。特别值得一提的是,它的多模态变体Qw…...

lingbot-depth-pretrain-vitl-14多场景落地:AR实时遮挡、3D重建、工业检测一文详解

lingbot-depth-pretrain-vitl-14多场景落地:AR实时遮挡、3D重建、工业检测一文详解 想象一下,你手里只有一部普通的手机摄像头,却想让它像人眼一样“感知”距离,知道哪个物体离你近,哪个离你远。或者,你有…...

GLM-OCR GPU算力优化实践:vLLM推理加速+令牌下采样,吞吐提升2.3倍

GLM-OCR GPU算力优化实践:vLLM推理加速令牌下采样,吞吐提升2.3倍 1. 项目背景与优化需求 GLM-OCR是一个基于GLM-V编码器-解码器架构构建的多模态OCR模型,专门为复杂文档理解而设计。这个模型集成了在大规模图文数据上预训练的CogViT视觉编码…...

Qwen3.5-9B-AWQ-4bit部署教程:双卡RTX 4090 D显存优化与AWQ量化优势解析

Qwen3.5-9B-AWQ-4bit部署教程:双卡RTX 4090 D显存优化与AWQ量化优势解析 1. 模型概述 Qwen3.5-9B-AWQ-4bit是一个支持图像理解的多模态模型,能够结合上传图片与文字提示词,输出中文分析结果。这个模型特别适合处理以下任务: 图…...

丹青幻境效果对比:Z-Image底座 vs SDXL在人物结构准确率与衣纹表现力评测

丹青幻境效果对比:Z-Image底座 vs SDXL在人物结构准确率与衣纹表现力评测 1. 评测背景与意义 在数字艺术创作领域,人物结构准确性和衣纹表现力一直是衡量AI绘画模型质量的关键指标。本次评测聚焦于丹青幻境采用的Z-Image底座与业界知名的SDXL模型&…...

Nunchaku FLUX.1-dev部署教程:Linux系统下CUDA驱动与PyTorch匹配指南

Nunchaku FLUX.1-dev部署教程:Linux系统下CUDA驱动与PyTorch匹配指南 想用最新的Nunchaku FLUX.1-dev模型生成惊艳的AI图片,结果卡在了环境配置上?别担心,这篇教程就是为你准备的。很多朋友在部署时遇到的最大障碍,往…...

RVC与ElevenLabs对比:开源可控性vs商业易用性深度分析

RVC与ElevenLabs对比:开源可控性vs商业易用性深度分析 想用AI克隆自己的声音,或者让喜欢的角色开口唱歌?现在市面上有两大主流选择:开源的RVC和商业化的ElevenLabs。一个免费但需要折腾,一个付费但开箱即用。到底哪个…...

千问3.5-27B效果实测:低质量扫描件文字区域检测与内容还原

千问3.5-27B效果实测:低质量扫描件文字区域检测与内容还原 1. 模型介绍 Qwen3.5-27B是Qwen官方发布的视觉多模态理解模型,具备强大的文本对话与图片理解能力。本镜像已在4张RTX 4090 D 24GB显卡环境下完成部署,提供中文Web对话界面、流式文…...

WuliArt Qwen-Image Turbo高清图展示:1024×1024下4K显示器100%缩放无模糊

WuliArt Qwen-Image Turbo高清图展示:10241024下4K显示器100%缩放无模糊 提示:本文所有展示图片均为WuliArt Qwen-Image Turbo模型直接生成,未经任何后期处理 1. 项目概述:重新定义个人GPU文生图体验 WuliArt Qwen-Image Turbo是…...

OpenClaw任务链设计:千问3.5-35B-A3B-FP8复杂流程自动化

OpenClaw任务链设计:千问3.5-35B-A3B-FP8复杂流程自动化 1. 为什么需要任务链自动化 上周我遇到一个典型的工作场景:需要从20份PDF报告中提取关键数据,整理成Excel表格,再根据这些数据生成分析图表,最后通过邮件发送…...

C++编程中new与delete操作符的深度解析

C编程中new与delete操作符的深度解析 在C编程的广阔天地里,内存管理是一个既基础又至关重要的环节。对于每一位C开发者而言,掌握内存的动态分配与释放是构建高效、稳定应用程序的基石。在众多内存管理工具中,new与delete操作符无疑是最为核心…...

C++编程中堆与栈内存的差异解析

C编程中堆与栈内存的差异解析 在C编程的世界里,内存管理是一个核心且至关重要的概念。其中,堆(Heap)与栈(Stack)作为两种主要的内存分配区域,各自扮演着不同的角色,理解它们之间的区…...

C++编程进阶:探索内建数学函数与C++20的<numbers>头文件

C编程进阶:探索内建数学函数与C20的头文件 在C编程的世界里,数学运算无处不在,无论是游戏开发中的物理模拟、图形处理,还是数据分析中的复杂计算,数学函数都是不可或缺的工具。随着C标准的不断演进,C20引入…...