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…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
以光量子为例,详解量子获取方式
光量子技术获取量子比特可在室温下进行。该方式有望通过与名为硅光子学(silicon photonics)的光波导(optical waveguide)芯片制造技术和光纤等光通信技术相结合来实现量子计算机。量子力学中,光既是波又是粒子。光子本…...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
