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”.


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/211392.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【20年扬大真题】试写一算法在带头结点的单链表结构上实现线性表操作LENGTH(L)

【20年扬大真题】 试写一算法在带头结点的单链表结构上实现线性表操作LENGTH&#xff08;L&#xff09;。 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdbool.h> #include<malloc.h> //单链表定义 //链表结点 int A[10] { 1,2,3,4,5,6,…

Hadoop学习总结(MapReduce的数据去重)

现在假设有两个数据文件 file1.txtfile2.txt2018-3-1 a 2018-3-2 b 2018-3-3 c 2018-3-4 d 2018-3-5 a 2018-3-6 b 2018-3-7 c 2018-3-3 c2018-3-1 b 2018-3-2 a 2018-3-3 b 2018-3-4 d 2018-3-5 a 2018-3-6 c 2018-3-7 d 2018-3-3 c 上述文件 file1.txt 本身包含重复数据&…

C++设计模式之工厂模式(上)——简单工厂模式

工厂模式 概述简单工厂模式介绍示例示例使用运行结果缺点 概述 工厂模式属于一种创建型设计模式。其可以分为简单工厂模式&#xff0c;工厂模式和抽象工厂模式。工厂模式分为上、中、下三篇&#xff0c;本篇主要介绍简单工厂模式。 简单工厂模式 介绍 简单工厂模式可以理解…

react中虚拟dom,diff,fiber - 初级了解

借鉴&#xff1a; 「React深入」一文吃透虚拟DOM和diff算法 - 掘金 (juejin.cn) 虚拟dom、fiber、渲染dom、dom-diff - 掘金 (juejin.cn) 未阅读源码&#xff0c;了解层面&#xff0c;后续可以深入了解 1.虚拟DOM ①.结构上&#xff1a;虚拟DOM比真实DOM轻很多 ②.操作上&…

JS逆向之wasm逆向(二)

本文仅供技术交流和技术学习 不做其他用途 接着上一篇继续讲&#xff1a; 上篇地址&#xff1a; JS逆向之wasm逆向(二进制) 网址&#xff1a; aHR0cHM6Ly93d3cuN3E2Y3lqLmNvbTo5MDAxL3JlZ2lzdGVyNDY5Njg/aV9jb2RlPTQ0Mjc5OTU1 这个网站我们后面可以继续讲他的debugger 和滑块…

玻色量子“揭秘”之多项式回归问题与QUBO建模

摘要&#xff1a;多项式回归&#xff08;Polynomial Regression&#xff09;是一种回归分析方法&#xff0c;通过拟合一个多项式方程来模拟自变量与因变量之间的非线性关系。多项式回归的目标是找到一组多项式系数&#xff0c;使得拟合曲线尽可能地接近数据点。这种方法可以用于…

本地websocket服务端暴露至公网访问【cpolar内网穿透】

本地websocket服务端暴露至公网访问【cpolar内网穿透】 文章目录 本地websocket服务端暴露至公网访问【cpolar内网穿透】1. Java 服务端demo环境2. 在pom文件引入第三包封装的netty框架maven坐标3. 创建服务端,以接口模式调用,方便外部调用4. 启动服务,出现以下信息表示启动成功…

centos7上用docker部署redis

1. 下载redis镜像 docker pull redis docker images # 查看镜像是否下载成功2. 安装redis容器 2.1 先准备好配置文件redis.conf vi /data/redis/redis.conf写入配置信息&#xff0c;appendonly yes&#xff0c;如果需要给redis配置密码&#xff0c;可以写入requirepass root…

Feign 远程调用

目录 代码架构 feign-api 模块解析 架构 依赖 定义接口类 lead-news-article模块 架构 yml配置 依赖 实现类 启动类 lead-news-wemedia模块 架构 调用 启动类 代码架构 feign-api 模块解析 架构 依赖 <dependency><groupId>org.springframework.clo…

为何越来越多的程序员纷纷转行网络安全?

目前&#xff0c;我国IT行业的人才结构不断升级&#xff0c;公司对程序员的要求越来越高&#xff0c;出现了大量的裁员现象&#xff0c;导致很多的程序员纷纷想转行的想法。 可能对于早期的程序员而言&#xff0c;学好编程语言就能找到比较好的工作。而现在伴随着互联网的不断发…

【深度学习实验】图像处理(一):Python Imaging Library(PIL)库:图像读取、写入、复制、粘贴、几何变换、图像增强、图像滤波

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 安装 PIL 库1. 图像读取和写入a. 图像读取b. 图像写入c. 构建新图像 2. 图像复制粘贴a. 图像复制b. 图像局部复制c. 图像粘贴 3. 几何变换a. 图像调整大小b. 图像旋转c. 图像翻转 4. 图像增强a.…

5个免费在线工具推荐

NSDT 三维场景建模工具GLTF/GLB在线编辑器Three.js AI自动纹理化开发包YOLO 虚幻合成数据生成器3D模型在线转换 1、NSDT 三维场景建模 访问地址&#xff1a;NSDT 编辑器 2、GLTF/GLB在线编辑器 访问地址&#xff1a;GLTF 编辑器 3、Three.js AI自动纹理化开发包 图一为原始模…