Linux之实现简易的shell

news2024/9/21 11:01:56

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


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文解读】在上下文中学习创建任务向量

一、简要介绍 大型语言模型&#xff08;LLMs&#xff09;中的上下文学习&#xff08;ICL&#xff09;已经成为一种强大的新的学习范式。然而&#xff0c;其潜在的机制仍未被很好地了解。特别是&#xff0c;将其映射到“标准”机器学习框架是具有挑战性的&#xff0c;在该框架中…

Python BDD 框架比较之 pytest-bdd vs behave

pytest-bdd和behave是 Python 的两个流行的 BDD 测试框架&#xff0c;两者都可以用来编写用户故事和可执行的测试用例&#xff0c; 具体选择哪一个则需要根据实际的项目状况来看。 先简单看一下两者的功能&#xff1a; pytest-bdd 基于pytest测试框架&#xff0c;可以与pytest…

美团技术博客即将十周岁啦 | 欢迎分享你跟它的故事

种一棵树最好的时间是十年前&#xff0c;其次是现在。 2013年12月04日&#xff0c; 美团技术博客发布了第一篇技术文章。 时光荏苒&#xff0c;岁月如歌。 美团技术博客即将迎来自己十周岁的生日。 感谢大家的一路相伴。 十年来&#xff0c;美团技术博客累计发布了570多篇技术文…

STM32_6(TIM)

TIM定时器&#xff08;第一部分&#xff09; TIM&#xff08;Timer&#xff09;定时器定时器可以对输入的时钟进行计数&#xff0c;并在计数值达到设定值时触发中断16位计数器、预分频器、自动重装寄存器的时基单元&#xff0c;在72MHz计数时钟下可以实现最大59.65s的定时不仅…

【深度学习】神经网络术语:Epoch、Batch Size和迭代

batchsize&#xff1a;中文翻译为批大小&#xff08;批尺寸&#xff09;。 简单点说&#xff0c;批量大小将决定我们一次训练的样本数目。 batch_size将影响到模型的优化程度和速度。 为什么需要有 Batch_Size : batchsize 的正确选择是为了在内存效率和内存容量之间寻找最…

WordPress站点屏蔽过滤垃圾评论教程(Akismet反垃圾评论插件)

前段时间我的WordPress站点经常收到垃圾评论的轰炸&#xff0c;严重时一天会收到几十条垃圾评论。我这个小破站一没啥流量&#xff0c;二又不盈利&#xff0c;实在是不太理解为啥有人要这么执着地浪费资源在上面。 Akismet反垃圾评论插件 其实用了 Akismet 反垃圾评论插件后&a…

java--static修饰成员变量

1.static 叫静态&#xff0c;可以修饰成员变量、成员方法。 2.成员变量按照有无static修饰&#xff0c;分为两种&#xff1a; ①类变量&#xff1a;有static修饰&#xff0c;属于类&#xff0c;在计算机里只有一份&#xff0c;会被类的全部对象共享(不管那个类调用的&#x…

Spring Security(安全框架,必须登录成功才能访问指定资源)

一、背景知识 1、Spring Security 是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean&#xff0c;充分利用了Spring IoC&#xff0c;DI&#xff08;IOC: 控制反转Inversion of Control ,DI:D…

97、Text2NeRF: Text-Driven 3D Scene Generation with Neural Radiance Fields

简介 论文地址 使用扩散模型来推断文本相关图像作为内容先验&#xff0c;并使用单目深度估计方法来提供几何先验&#xff0c;并引入了一种渐进的场景绘制和更新策略&#xff0c;保证不同视图之间纹理和几何的一致性 实现流程 简单而言&#xff1a; 文本-图片扩散模型生成一…

在PCL视图器中使用随机生成的颜色来可视化一组匹配的点对

std::vector<Eigen::Vector2d> centroids_unknown_motion_underk; std::vector<Eigen::Vector2d> centroids_unknown_motion_k; // 进行数字填充 pcl::visualization::PCLVisualizer viewer("Centroid Visualization");int id 0;// 添加 XY 坐标系doub…

Okhttp 浅析

安全的连接 OkHttpClient: OkHttpClient: 1.线程调度 2.连接池,有则复用,没有就创建 3.interceptor 4.interceptor 5.监听工厂 6.是否失败重试 7.自动修正访问,如果没有权限或认证 8是否重定向 followRedirects 9.协议切换时候是否继续重定向 10.Cookie jar 容器 默认…

软件开发及交付中,如何平衡项目进度和团队成员的利益?

在平衡软件质量与时间、成本、范围的关系时&#xff0c;需要考虑到项目管理的金三角概念&#xff0c;即时间、成本和范围。从项目管理的角度来看&#xff0c;项目进度和团队成员的利益需要平衡。 以下是一些建议&#xff1a; 制定可行的计划&#xff1a;让项目相关各方充分参与…

Java面试-框架篇-Mybatis

Java面试-框架篇-Mybatis MyBatis执行流程延迟加载使用及原理一, 二级缓存来源 MyBatis执行流程 读取MyBatis配置文件: mybatis-config.xml加载运行环境和映射文件构造会话工厂SqlSessionFactory会话工厂创建SqlSession对象(包含了执行SQL语句的所有方法)操作数据库的接口, Ex…

Retrofit 原理浅析 二

类型安全的连接 enqueue : 异步 切线程,会在队列中执行 execute : 同步 不切线程 Retrofit:Create 1.验证是否是接口 否则是类或者其他则报错 API Declarations must be interfaces 2.check . add 取出进行处理,然后添加到Colltions 中,如果有父接口则报错,不能是泛型 添加…

【uniapp】部分图标点击事件无反应

比如&#xff1a;点击这个图标在h5都正常&#xff0c;在小程序上无反应 css&#xff1a;也设置z-index&#xff0c;padding 页面上也试过click.native.stop.prevent"changePassword()" 时而可以时而不行&#xff0c; 最后发现是手机里输入键盘的原因&#xff0c;输…

pcie-2-rj45速度优化

背景: 目前用iperf3打流传输速率达不到要求,千兆实际要求跑到800M以上: 优化方案: 1.优化defconfig: 首先编译user版本验证看是否正常 debug版本关闭CONFIG_SLUB_DEBUG_ON宏控。 2.找FAE ,通过更换驱动,或者更新驱动来优化 3.绑定大核: 以8125网卡为例,udp…

【计算机网络笔记】数据链路层概述

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

开启数据库审计(db,extended级别或os级别),并将审计文件存放到/home/oracle/audit下

文章目录 开启数据库审计&#xff08;db,extended级别或os级别&#xff09;&#xff0c;并将审计文件存放到/home/oracle/audit下一. 简介二. 配置2.1. 审计是否安装2.2. 审计表空间迁移2.3. 审计参数2.4. 审计级别2.5. 其他审计选项2.6. 审计相关视图 三. 使用3.1. 开启/关闭审…

【已解决】微信小程序腾讯地图的map清除markers,setData将marker置空后,安卓和ios还会显示上次的内容的问题所在以及解决办法

问题描述 1.我首先点击了这个marker 2.这里可以看到根据id获取到了他的信息 3.当我滑动了地图&#xff0c;这时候重新加载了markers&#xff0c;我再次点击这个marker 4.会发现获取不到数据了 问题原因 个人猜测引起这个问题的原因是id重叠了&#xff0c;导致获取不到数据&am…

【shell】函数和数组的原理及使用注意

目录 一、函数 1.1函数的优点&#xff1a; 1.2如何定义函数&#xff08;shell&#xff09; 1.3 演示函数的作用以及增删改查 关于函数的使用 关于declare查询的用法 关于函数的增加与调用 关于函数的返回值return 关于echo作为返回 关于函数的参数传递 关于函数的环…