Linux之实现简易的shell
1.打印提示符并获取命令行
我们在使用shell的时候,发现我们在输入命令是,前面会有:有用户名,版本,当前路径等信息,这里我们可以用环境变量去获取:

1 #include <stdio.h>2 #include <stdlib.h>3 4 const char* getUsername()5 {6 const char* name = getenv("USER");7 if(name) return name;8 else return "none";9 }10 11 const char* getHostname()12 {13 const char* hostname = getenv("HOSTNAME");14 if(hostname) return hostname;15 else return "none";16 }17 18 const char* getCwd()19 {20 const char* cwd = getenv("PWD");21 if(cwd) return cwd;22 else return "none";23 }24 25 int main()26 {27 printf("%s@%s %s\n",getUsername(),getHostname(),getCwd()); 28 return 0;29 }
写。
看到我们打印出来的是绝对路径, 而shell显示的相对路径, 但为了区分先这样不去裁剪.
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #define NUM 1024 6 7 const char* getUsername() 8 { 9 const char* name = getenv("USER"); 10 if(name) return name; 11 else return "none"; 12 } 13 14 const char* getHostname() 15 { 16 const char* hostname = getenv("HOSTNAME"); 17 if(hostname) return hostname; 18 else return "none"; 19 } 20 21 const char* getCwd() 22 { 23 const char* cwd = getenv("PWD"); 24 if(cwd) return cwd; 25 else return "none"; 26 } 27 28 int getUsercommand(char* command, int num) 29 { 30 printf("[%s@%s %s]",getUsername(),getHostname(),getCwd()); 31 char* r = fgets(command,num,stdin);//最终还是会输入\n32 if(r == NULL) return 1;33 34 command[strlen(command)-1] = '\0';//去除输入的换行35 return 0; 36 } 37 38 int main() 39 { 40 char usercommand[NUM];41 //1.打印提示符并且获取命令字符串42 getUsercommand(usercommand,sizeof(usercommand));43 //2. 44 //3. 45 printf("%s",usercommand);//回显命令,用于测试46 return 0;47 }
由于用scanf接收的遇到空格就会停止读取, 所以用fgets, 而且用户输入完命令一定会输入回车, 所以把最后一个回车符删掉.
2.解析命令行
我们在输入命令时, 可能不仅仅只是一段,比如说:"ls -a -l "。但是命令行解释器内部在解析指令时应该传递的是"ls" "-a" "-l"这样的多个个字符串, 所以我们还需要以空格来分割字符串。
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 5 #define DEBUG 16 #define NUM 10247 #define SIZE 648 #define SEP " "41 void commandSplit(char* in, char* out[])42 {43 int argc = 1;44 out[0] = strtok(in,SEP);
W> 45 while(out[argc++] = strtok(NULL,SEP));//报警不需要处理46 47 #ifdef DEBUG 48 for(int i = 0; out[i]; i++)49 printf("%d:%s\n",i,out[i]);50 #endif51 }52 53 int main()54 {55 char usercommand[NUM];56 char* argv[SIZE];57 //1.打印提示符并且获取命令字符串58 getUsercommand(usercommand,sizeof(usercommand));59 //2.分割字符串60 commandSplit(usercommand, argv); 61 //3. 62 return 0;63 }

3.执行对应的命令
创建子进程和进程替换, 为了不影响shell, 我们将大部分指令的执行让子进程去完成, 父进程只要阻塞等待子进程完成就好了。
1 #include <stdio.h>2 #include <stdlib.h>3 #include <string.h>4 #include <unistd.h>5 #include <sys/types.h>6 #include <sys/wait.h>7 8 //#define DEBUG 19 #define NUM 102410 #define SIZE 6411 #define SEP " "12 13 const char* getUsername()14 {15 const char* name = getenv("USER");16 if(name) return name;17 else return "none";18 }19 20 const char* getHostname()21 {22 const char* hostname = getenv("HOSTNAME");23 if(hostname) return hostname;24 else return "none";25 }26 27 const char* getCwd()28 {29 const char* cwd = getenv("PWD");30 if(cwd) return cwd;31 else return "none";32 }33 34 int getUsercommand(char* command, int num) 35 {36 printf("[%s@%s %s]",getUsername(),getHostname(),getCwd()); 37 char* r = fgets(command,num,stdin);//最终还是会输入\n38 if(r == NULL) return -1;39 40 command[strlen(command)-1] = '\0';//去除输入的换行41 return strlen(command);42 }43 44 void commandSplit(char* in, char* out[])45 {46 int argc = 1;47 out[0] = strtok(in,SEP);
W> 48 while(out[argc++] = strtok(NULL,SEP));//报警不需要处理49 50 #ifdef DEBUG 51 for(int i = 0; out[i]; i++)52 printf("%d:%s\n",i,out[i]);53 #endif54 }55 56 int execute(char* argv[])57 {58 pid_t id = fork();59 if(id < 0) return 1;60 else if(id == 0)61 {62 //child63 //exec commond64 execvp(argv[0],argv);65 exit(1);66 }67 68 else69 {70 //father71 pid_t rid = waitpid(id,NULL,0);72 if(rid < 0)73 printf("wait fail\n");74 }75 76 return 0;77 }78 79 int main()80 {81 while(1)82 {83 char usercommand[NUM];84 char* argv[SIZE];85 //1.打印提示符并且获取命令字符串86 int n = getUsercommand(usercommand,sizeof(usercommand));87 if(n <= 0) continue;//如果得到的是空串或者获取失败,不要往后执行88 //2.分割字符串89 commandSplit(usercommand, argv);90 //3.执行命令91 execute(argv); 92 }93 return 0;94 }
由于shell要一直运行, 所以要循环执行, 这里程序替换用execvp函数比较合适, 因为argv数组就是我们分割出的一个个命令的子串, argv[0]就是程序名, argv就是指令集. 父进程只进行wait即可.
此外, getUsercommand函数可以优化一下, 返回的是输入的指令的长度, 如果接收失败(返回值为-1或者返回值是0只打印了空行)就不需要往下执行了, 直接continue进行下一轮.

4.特殊处理
有一批命令, 不能让子进程执行, 必须让父进程自己执行, 这些命令叫内建命令.
1) cd指令

可以看到cd .. 之后并没有发生什么异常, 但是pwd之后发现路径没有发生变化.
我们为什么能在linux中进入某个目录, 就是因为我们改变了shell的工作目录. 每个进程都有自己的工作目录, 我们想让父进程的工作目录发生改变, 但是程序替换之后都是子进程在执行cd .., 改变的都是子进程的工作目录, 子进程改变完了又被回收了, 父进程完全没发生变化, 所以cd应该实现成内建命令。
79 void cd(const char* path)80 {81 chdir(path);82 }83 84 //1->yes,0->no85 int doBuildin(char* argv[])86 {87 if(strcmp(argv[0],"cd") == 0)88 {89 char* path = NULL;
W> 90 if(argv[1] == NULL) path = ".";91 else path = argv[1];92 cd(path);93 return 1;94 }95 else if(strcmp(argv[0],"ls")==0)96 {97 return 1;98 }99 return 0; 100 }101 102 int main()103 {104 while(1)105 {106 char usercommand[NUM];107 char* argv[SIZE];108 //1.打印提示符并且获取命令字符串109 int n = getUsercommand(usercommand,sizeof(usercommand));110 if(n <= 0) continue;//如果得到的是空串或者获取失败,不要往后执行111 //2.分割字符串112 commandSplit(usercommand, argv);113 //3.检查是不是内建命令,是的话直接执行114 n = doBuildin(argv);115 if(n) continue;//是内建命令不用往后执行了116 //4.执行命令117 execute(argv);118 }119 return 0;120 }
所以在执行命令前先检查是不是内建命令, 用返回值接收, 如果是就直接执行并返回1, continue不往下执行, 如果不是就返回0, 执行命令.
既然当前的工作目录改变了, 那么环境变量PWD也要改变:


chdir改变当前工作目录, getcwd获取当前的工作路径, sprintf将tmp中的内容输出到cwd中, putenv将cwd导入环境变量.

2) export命令

创建一个数组env储存要导入的环境变量, 设置size指向导入到第几个环境变量.
如果argv[1]是空就直接返回, 否则就导入环境变量, 注意不能直接把argv[1]导入进去, 因为argv[1]随着指令的输入时刻在变化, 需要开辟额外的空间去存储.
3)echo指令
![]()


4)ls指令
我们执行的ls指令中不同的文件都有不同的颜色,所以对于ls我们可以在分割命令的时候加上一个“--color=auto”.


相关文章:
Linux之实现简易的shell
1.打印提示符并获取命令行 我们在使用shell的时候,发现我们在输入命令是,前面会有:有用户名,版本,当前路径等信息,这里我们可以用环境变量去获取: 1 #include <stdio.h>2 #include <stdlib.h>…...
如何实现在公网下使用navicat图形化工具远程连接本地内网的MariaDB数据库
公网远程连接MariaDB数据库【cpolar内网穿透】 文章目录 公网远程连接MariaDB数据库【cpolar内网穿透】1. 配置MariaDB数据库1.1 安装MariaDB数据库1.2 测试局域网内远程连接 2. 内网穿透2.1 创建隧道映射2.2 测试随机地址公网远程访问3. 配置固定TCP端口地址3.1 保留一个固定的…...
MySQL InnoDB 引擎底层解析(三)
6.3.3. InnoDB 的内存结构总结 InnoDB 的内存结构和磁盘存储结构图总结如下: 其中的 Insert/Change Buffer 主要是用于对二级索引的写入优化,Undo 空间则是 undo 日志一般放在系统表空间,但是通过参数配置后,也可以用独立表空 间…...
浅析基于智能音视频技术的城市重要场馆智能监控系统设计
了解旭帆科技的朋友都知道,旭帆科技一直都乐于和大家分享各类场景的视频解决方案,今天小编就基于智能音视频技术的城市重要场馆智能监控系统设计和大家探讨一下。 基于智能音视频技术的城市重要场馆智能监控系统设计,主要包含以下要素&#x…...
hdu-lcy算法培训班 入门第一讲 数学基础
习题 F题...
获取ip属地(ip2region本地离线包-超简单)
背景 最近有涉及要显示ip属地,但我想白嫖,结果就是白嫖的api接口太慢了,要延迟3到4秒左右,很影响体验,而且不一定稳定。 结果突然看到了这个【ip2region】开源项目,离线识别ip属地,精度自己测…...
主流的低代码平台有哪些?程序员应该如何与低代码相处?
本文主要阐述低代码的概念,介绍目前主流的低代码平台,总结低代码平台的典型特征、存在优势以及未来发展趋势。并站在程序员的角度,分析如何在已经到来的低代码战争中,找到自己的定位,一展所长。 什么是低代码ÿ…...
华为---OSPF网络虚连接(Virtual Link)简介及示例配置
【1】OSPF网络虚连接(Virtual Link)简介 为了避免区域间的环路,OSPF规定不允许直接在两个非骨干区域之间发布路由信息,只允许在一个区域内部或者在骨干区域和非骨干区域之间发布路由信息。因此,每个ABR都必须连接到骨干…...
Python函数式编程:让你的代码更优雅更简洁
概要 函数式编程(Functional Programming)是一种编程范式,它将计算视为函数的求值,并且避免使用可变状态和循环。 函数式编程强调的是函数的计算,而不是它的副作用。 在函数式编程中,函数是第一类公民&a…...
艺术作品3D虚拟云展厅能让客户远程身临其境地欣赏美
艺术品由于货物昂贵、易碎且保存难度大,因此在艺术品售卖中极易受时空限制,艺术品三维云展平台在线制作是基于web端将艺术品的图文、模型及视频等资料进行上传搭配,构建一个线上艺术品3D虚拟展厅,为艺术家和观众提供了全新的展示和…...
负载均衡简介
负载均衡 负载均衡(Load Balance,简称 LB)是高并发、高可用系统必不可少的关键组件,目标是 尽力将网络流量平均分发到多个服务器上,以提高系统整体的响应速度和可用性。 负载均衡的分类和OSI模型息息相关,…...
【高级网络程序设计】Week2-1 Sockets
一、The Basics 1. Sockets 定义An abstraction of a network interface应用 use the Socket API to create connections to remote computers send data(bytes) receive data(bytes) 2. Java network programming the java network libraryimport java.net.*;similar to…...
quickapp_快应用_requestHeader
和客户端相同,在进行请求交互中,后端会需要获取当前设备信息,此时需要使用应用上下文app与设备信息 应用版本号 const app require(system.app)app.getInfo().versionName // versionName:应用版本名称 (manifest.json中versio…...
FPGA----ZCU106使用petalinux 2019.1的第一个app开发
1、petalinux在zcu106上的构建参见前文 FPGA----ZCU106使用petalinux 2019.1(全网最详)-CSDN博客文章浏览阅读31次。本文完成了Vivado 2019.1版本下的基于ZCU106的全部linux系统移植https://blog.csdn.net/qq_37912811/article/details/1345197352、我们…...
华为ac+fit漫游配置案例
Ap漫游配置: 其它配置上面一样,ap管理dhcp和业务dhcp全在汇聚交换机 R1: interface GigabitEthernet0/0/0 ip address 11.1.1.1 255.255.255.0 ip route-static 12.2.2.0 255.255.255.0 11.1.1.2 ip route-static 192.168.0.0 255.255.0.0 11.1.1.2 lsw1: vlan batch 100 200…...
Jenkins 配置节点交换内存
查看交换内存 free -hswapon -s创建swap文件 dd if/dev/zero of/mnt/swap bs1M count1024启用交换文件 设置权限 chmod 600 /mnt/swap设置为交换空间 mkswap /mnt/swap启用交换 swapon /mnt/swap设置用户组 chown root:root /mnt/swap查看 swapon -s重启系统也能生效还需要修…...
二百零七、Flume——Flume实时采集5分钟频率的Kafka数据直接写入ODS层表的HDFS文件路径下
一、目的 在离线数仓中,需要用Flume去采集Kafka中的数据,然后写入HDFS中。 由于每种数据类型的频率、数据大小、数据规模不同,因此每种数据的采集需要不同的Flume配置文件。玩了几天Flume,感觉Flume的使用难点就是配置文件 二、…...
【实验】配置用户自动获取IPv6地址的案例
【赠送】IT技术视频教程,白拿不谢!思科、华为、红帽、数据库、云计算等等编辑https://xmws-it.blog.csdn.net/article/details/117297837?spm1001.2014.3001.5502https://xmws-it.blog.csdn.net/article/details/117297837?spm1001.2014.3001.5502【…...
手撕A*算法(详解A*算法)
A*算法原理 全局路径规划算法,根据给定的起点和终点在全局地图上进行总体路径规划。 导航中使用A*算法计算出机器人到目标位置的最优路线,一般作为规划的参考路线 // 定义地图上的点 struct Point {int x,y; // 栅格行列Point(int x, int y):x(x),y(y){…...
1688API如何获取商品详情信息(关键词搜索商品列表),1688API接口开发系列
1688商品详情接口是指1688平台提供的API接口,用于获取商品详情信息。通过该接口,您可以获取到商品的详细信息,包括商品标题、价格、库存、描述、图片等。 要使用1688商品详情接口,您需要先申请1688的API权限,并获取ac…...
深度解析w64devkit:Windows平台便携式C/C++开发套件完全掌握指南
深度解析w64devkit:Windows平台便携式C/C开发套件完全掌握指南 【免费下载链接】w64devkit Portable C and C Development Kit for x64 (and x86) Windows 项目地址: https://gitcode.com/gh_mirrors/w6/w64devkit w64devkit是一款专为Windows平台设计的便携…...
零基础部署Qwen3-ASR-0.6B:支持52种语言的语音识别模型快速上手
零基础部署Qwen3-ASR-0.6B:支持52种语言的语音识别模型快速上手 1. 为什么选择Qwen3-ASR-0.6B 语音识别技术正在改变我们与设备交互的方式。Qwen3-ASR-0.6B作为一款轻量级但功能强大的语音识别模型,特别适合想要快速搭建多语言语音识别系统的开发者。它…...
注册表惹的祸?深度解析Windows 11软件打开方式失效的底层逻辑与一劳永逸的预防方案
Windows 11打开方式失效的底层机制分析与系统级预防策略 每次双击文档却弹出一堆重复选项,或是根本打不开关联程序——这种困扰在Windows 11用户中相当普遍。表面看是简单的文件关联问题,实则暴露了Windows注册表管理的深层机制缺陷。本文将带您穿透现象…...
Joy-Con Toolkit完整指南:免费开源工具让你的Switch手柄焕然一新
Joy-Con Toolkit完整指南:免费开源工具让你的Switch手柄焕然一新 【免费下载链接】jc_toolkit Joy-Con Toolkit 项目地址: https://gitcode.com/gh_mirrors/jc/jc_toolkit Joy-Con Toolkit是一款专为任天堂Switch手柄设计的开源工具箱,让普通玩家…...
Spring Boot 异步任务调度
Spring Boot 异步任务调度:提升应用性能的利器 在现代Web应用中,高并发和快速响应是开发者追求的核心目标之一。Spring Boot作为Java生态中最流行的框架之一,其异步任务调度功能为开发者提供了一种高效处理耗时任务的解决方案。通过异步执行…...
Python+OpenCV 计算机视觉:从零入门 AI 视觉开发
📝 本章学习目标:从零掌握 PythonOpenCV 计算机视觉基础,从环境搭建到实战项目,覆盖图像处理、特征检测、目标识别、视频分析全流程,可直接落地 AI 视觉开发项目。一、引言:为什么计算机视觉是 AI 核心赛道…...
环形振荡器熵源设计与FPGA安全实现
1. 环形振荡器作为熵源的工程实践解析在硬件安全领域,环形振荡器(Ring Oscillator, RO)作为物理熵源的应用已有二十余年历史。我曾在多个FPGA安全项目中采用RO方案,直到2013年的一次侧信道攻击测试中,发现某型号RO在连…...
Codex CLI教程(五) | MCP 之 Context7
Codex CLI教程(五) | MCP 之 Context7前言第一章:MCP 接入前置要求第二章:MCP 接入方式2.1 方式一:本地接入2.2 方式二:远程接入(推荐)前言 给 Codex 接入 Context7,常见…...
Universal-G-Code-Sender多语言支持教程:打造国际化CNC操作界面
Universal-G-Code-Sender多语言支持教程:打造国际化CNC操作界面 【免费下载链接】Universal-G-Code-Sender A cross-platform G-Code sender for GRBL, Smoothieware, TinyG and G2core. 项目地址: https://gitcode.com/gh_mirrors/un/Universal-G-Code-Sender …...
万亿参数模型Ring-1T:MoE架构与强化学习突破
1. 万亿参数模型Ring-1T的技术突破在人工智能领域,大型语言模型的发展已经进入了一个全新的阶段。最近,Inclusion AI团队发布了Ring-1T,这是首个开源的、拥有万亿参数规模的思维模型。这个突破性的成果不仅在模型规模上创造了新的记录&#x…...
