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);//最终还是会输入\n
 32     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 1
    6 #define NUM 1024
    7 #define SIZE 64
    8 #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 #endif
   51 }
   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 1
    9 #define NUM 1024
   10 #define SIZE 64
   11 #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);//最终还是会输入\n
   38     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 #endif
   54 }
   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         //child
   63         //exec commond
   64         execvp(argv[0],argv);
   65         exit(1);
   66     }
   67 
   68     else
   69     {
   70         //father
   71         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->no
   85 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”.


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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

检验LIS系统:医院信息管理的重要组成部分

检验LIS系统源码&#xff0c;云LIS系统源码 云LIS系统是医院信息管理的重要组成部分之一&#xff0c;集申请、采样、核收、计费、检验、审核、发布、质控、查询、耗材控制等检验科工作为一体的网络管理系统。LIS系统不仅是自动接收检验数据&#xff0c;打印检验报告&#xff0c…

因果发现31种高效经典方案汇总,附配套算法和代码

因果发现&#xff08;Causal Discovery&#xff09;是一个复杂的过程&#xff0c;其目标是从大量的数据中确定变量之间的因果关系。这个过程通常涉及到的是如何从纷繁复杂的数据中发现其中隐含的因果关系。有时&#xff0c;研究者可以通过随机实验进行干预来发现因果关系&#…

Windows Python3安装salt模块失败处理

复现CVE-2020-11651时候运行CVE-2020-11651的poc时候需要salt模块 在下载时出现了错误 尝试在网上寻找解决方法&#xff1a; 1.更新 setuptools 和 wheel pip install --upgrade setuptools wheel 2. 安装Microsoft Visual C 14.0 因为salt模块包包使用了 C/C 扩展&#x…

【速看】如何提高微信权重?影响微信权重的加分、扣分行为

微信具有一套权重判定系统&#xff0c;类似于搜索引擎的PR值&#xff0c;可以看做是一个“积分系统”。好的操作会增加积分&#xff0c;负面操作会减少积分。 当积分低于特定标准&#xff08;即底线&#xff09;时&#xff0c;将会被严重惩罚或封号。这样&#xff0c;微信确保了…

C# Onnx PP-Vehicle 车辆分析(包含:车辆检测,识别车型和车辆颜色)

目录 效果 模型信息 mot_ppyoloe_s_36e_ppvehicle.onnx vehicle_attribute_model.onnx 项目 代码 下载 其他 C# Onnx PP-Vehicle 车辆分析&#xff08;包含&#xff1a;车辆检测&#xff0c;识别车型和车辆颜色&#xff09; 效果 模型信息 mot_ppyoloe_s_36e_ppvehi…

代码随想录算法训练营Day 59 || 503.下一个更大元素II、42. 接雨水

503.下一个更大元素II 力扣题目链接(opens new window) 给定一个循环数组&#xff08;最后一个元素的下一个元素是数组的第一个元素&#xff09;&#xff0c;输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序&#xff0c;这个数字之后的第一个比它更…

docker 安装常用环境

一、 安装linux&#xff08;完整&#xff09; 目前为止docker hub 还是被封着&#xff0c;用阿里云、腾讯云镜像找一找版本直接查就行 默认使用latest最新版 #:latest 可以不写 docker pull centos:latest # 拉取后查看 images docker images #给镜像设置标签 # docker tag […

某基金公司赵哥“逆袭”了!!!

赵哥&#xff0c;在上海一家基金公司做运维主管。 平时工作的首要任务&#xff0c;就是保障公司各项信息系统的安全运行。 万一系统运行中出现了一些重要问题&#xff0c;他还要负责进行调查、记录与汇报... 总之&#xff0c;责任很重&#xff0c;该说不说&#xff0c;搞不好…

10.分组循环练习题

分组循环 https://leetcode.cn/problems/longest-even-odd-subarray-with-threshold/solutions/2528771/jiao-ni-yi-ci-xing-ba-dai-ma-xie-dui-on-zuspx/?envTypedaily-question&envId2023-11-16 分组循环 适用场景&#xff1a; 按照题目要求&#xff0c;数组会被分割成若…

大型养殖场需要哪些污水处理设备

大型养殖场是一个涉及环境保护和可持续发展的关键行业&#xff0c;对于处理养殖场产生的污水有着明确的要求和标准。为了确保污水得到有效处理和处理效果达到国家排放标准&#xff0c;大型养殖场需要配备一系列污水处理设备。以下是几种常见的污水处理设备&#xff1a; 1. 水解…

厦门市委常委、常务副市长黄晓舟调研极狐(GitLab)

11 月 22 日&#xff0c;厦门市委常委、常务副市长黄晓舟&#xff0c;厦门市工信局副局长许文恭&#xff0c;厦门市高新技术创业中心有限公司董事长邸国栋等一行人员莅临极狐(GitLab)进行参观调研&#xff0c;深入了解极狐(GitLab)的发展情况。 黄晓舟副市长&#xff08;左&…

TikTok历史探秘:短视频中的时间之旅

在数字时代的浪潮中&#xff0c;TikTok崭露头角&#xff0c;成为社交媒体领域的一颗耀眼新星。这款短视频应用以其独特的创意、时尚和娱乐性质&#xff0c;吸引了全球数以亿计的用户。 然而&#xff0c;TikTok并非一夜之间的奇迹&#xff0c;它背后蕴藏着丰富而有趣的历史故事…

解决ElementUI时间选择器回显出现Wed..2013..中国标准时间.

使用饿了么组件 时间日期选择框回显到页面为啥是这样的&#xff1f; 为什么再时间框中选择日期&#xff0c;回显页面出现了这种英文格式呢&#xff1f;&#xff1f;&#xff1f;&#xff1f; 其实这个问题直接使用elementui的内置属性就能解决 DateTimePicker 日期时间选择…

qs-一个序列化和反序列化的JavaScript库

起因 一个业务场景中&#xff0c;最终得到一串字符"status[0]value1&status[1]value2" 通过解析&#xff0c;理应得到一个数组&#xff0c;却得到一个对象 于是展开问题排查 最终发现是qs.parse 这个地方出了问题 排查结果 qs解析这种带下标的字符串时&#xff…

内网穿透隐秘隧道搭建

别低头&#xff0c;皇冠会掉&#xff1b;别流泪&#xff0c;贱人会笑。 本文首发于先知社区&#xff0c;原创作者即是本人 0x00 前言 构建内网隐蔽通道&#xff0c;从而突破各种安全策略限制&#xff0c;实现对目标服务器的完美控制。 当我们从外网成功获得攻击点的时候&…

实时截留抖音询价的用户:10个合规方法,让你的业务迅速增长!

先来看实操成果&#xff0c;↑↑需要的同学可看我名字↖↖↖↖↖&#xff0c;或评论888无偿分享 一、引言 随着抖音的普及度越来越高&#xff0c;越来越多的商家开始关注抖音询价用户。这些潜在客户对于企业的发展至关重要&#xff0c;如何实时截留这些用户成为商家关注的重点…

leetcode:645. 错误的集合(python3解法)

难度&#xff1a;简单 集合 s 包含从 1 到 n 的整数。不幸的是&#xff0c;因为数据错误&#xff0c;导致集合里面某一个数字复制了成了集合里面的另外一个数字的值&#xff0c;导致集合 丢失了一个数字 并且 有一个数字重复 。 给定一个数组 nums 代表了集合 S 发生错误后的结…

python避坑指南(更新中)

os.path.join 避免连续的/&#xff0c;看示例即清楚&#xff0c;最好的避免方法是字符串首末都不要加’/&#xff1a; join用法 用join前面的符号将参数数组里面的字符串连接起来&#xff0c;注意join只有一个参数

合并两个有序链表,剑指offer,力扣

目录 力扣题目地址&#xff1a; 原题题目&#xff1a; 我们直接看题解吧&#xff1a; 解题方法&#xff1a; 审题目事例提示&#xff1a; 解题思路&#xff1a; 具体流程如下&#xff1a; 代码实现&#xff1a; 知识补充&#xff1a; 力扣题目地址&#xff1a; 21. 合并两个有序…

品牌如何利用情绪营销打出知名度

“悦己文化”和“她经济”的兴起让人们更加关注自己的内心感受,同时“发疯文学”、“精神内耗”等热词都体现了当代人为了缓解压力而为情绪消费的趋势&#xff0c;品牌想要留住消费者&#xff0c;就必须不断迭代&#xff0c;直面消费者需求&#xff0c;今天媒介盒子就来和大家聊…