(18)Linux 实现简易版shell

news2024/11/26 18:55:14

前言:做一个 "会创建,会终止,会等待,会程序替换" 的简易 shell 。

1、显示提示符和获取用户输入

shell 本质就是个死循环,我们不关心获取这些属性的接口,如果要实现 shell:

  • 1:显示提示符 →  #
  • 2:获取用户输入 → fgets
  • 3:将接收到的字符串拆开  →  把 "ls -a -l" 转换成  "ls"  "-a"  "-l" 
  • ……

我们先从简单的入手,先来实现前两步,显示提示符 获取用户输入

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <unistd.h>
  5 #include <sys/wait.h>
  6 #include <sys/types.h>
  7 
  8 #define NUM 1024
  9 
 10 char cmd_line[NUM];   // 用来接收命令行内容
 11 
 12 int main(void)
 13 {
 14   //0.命令行解释器,一定是一个常驻内存的进程,不退出
 15     while (1) {
 16         /* 1:显示提示信息 */
 17         printf("[amx@hecs-0-1  myshell] # ");
 18         fflush(stdout);
 19 
 20         /* 2:获取用户输入,输入的是各种指令和选项 :"ls -a -l "*/
 21         memset (
 22             cmd_line,
 23             '\0',
 24             sizeof(cmd_line)
 25             );
 26        if( fgets(cmd_line, NUM, stdin)==NULL) {
 27           continue;
 28        }/* 从键盘获取,标准输入,stdin 
 29             获取到 C 风格的字符串,默认添加 '\0' */
 30         printf("%s\n", cmd_line);                                                                                                                                  
 31     }
 32 }

我们利用 fgets 函数从键盘上获取,标准输入 stdin,获取到 C 风格的字符串,

注意默认会添加 \0 ,我们先把获取到的结果 cmd_line 打印出来看看:

因为 cmd_line 里有一个 \n——我们输入完指令后会按下回车,printf里面又有一个回车  。我们把它替换成 \0 即可: 

cmd_line[strlen(cmd_line) - 1] = '\0';  //-1的目的是,最后一个字符的下标是长度减一,strlen不包括\n,\0这些 

2、将接收到的字符串拆开

下面我们需要 将接收到的字符串拆开,比如:把 "ls -a -l" 拆成  "ls"  "-a"  "-l"  

因为 exec 函数簇无论是列表传参还是数组传参,一定是要逐个传递的!

我们自己实现的话,就是把这些空格变成\0,让他们输出。

但是我们可以使用 strtok 函数,将一个字符串按照特定的分隔符打散,将子串依次返回:

char* strtok(char* str, const char* delim);

代码:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <stdlib.h>
  4 #include <unistd.h>
  5 #include <sys/wait.h>
  6 #include <sys/types.h>
  7  
  8 #define NUM 1024
  9 #define SIZE 32
 10 #define SEP " "//设置分隔符 空格
 11 
 12 //保存打散之后的命令行字符串
 13 char *g_argv[SIZE];
 14 char cmd_line[NUM];   // 用来接收完整的命令行内容
 15  
 16 int main(void)
 17 {
 18   //0.命令行解释器,一定是一个常驻内存的进程,不退出
 19     while (1) {
 20         /* 1:显示提示信息 */
 21         printf("[amx@hecs-0-1  myshell] # ");
 22         fflush(stdout);
 23                                                                                                                                      
 24         /* 2:获取用户输入,输入的是各种指令和选项 :"ls -a -l "*/                                                                     
 25         memset (                                                                                                                     
 26             cmd_line,                                                                                                                
 27             '\0',                                                                                                                    
 28             sizeof(cmd_line)                                                                                                         
 29             );                                                                                                                       
 30        if( fgets(cmd_line, NUM, stdin)==NULL) {                                                                                      
 31           continue;                                                                                                                  
 32        }/* 从键盘获取,标准输入,stdin                                                                                               
 33             获取到 C 风格的字符串,默认添加 '\0' */                                                                                  
 34         cmd_line[strlen(cmd_line) - 1] = '\0';  //-1的目的是,最后一个字符的下标是长度减一,strlen不包括\n,\0这些                    
 35       // printf("%s\n", cmd_line);                                                                                                   
 36       //3.命令行字符串解析:"ls -a -l "->"ls" "-a" "-l"                                                                              
 37       g_argv[0]= strtok(cmd_line, SEP);//第一次调用,要传入原始字符串  
 38       int index=1;
 39     while(g_argv[index++]=strtok(NULL,SEP));//第二次调用,如果还要解析原始字符串,传NULL
 40        //for debug 
 41          for(index=0;g_argv[index];index++){             
 42           printf("g_argv[%d]:%s\n",index,g_argv[index]);                                                                           
 43         }                                                                                                                                           
 44     }                                                                                                                                                            
 45 }                                                                                                                                                         
~      

运行结果:

3、创建进程 & 程序替换 

下面我们实现 创建进程,执行它。 

 1 #include <stdio.h>
    2 #include <string.h>
    3 #include <stdlib.h>
    4 #include <unistd.h>
    5 #include <sys/wait.h>
    6 #include <sys/types.h>
    7  
    8 #define NUM 1024
    9 #define SIZE 32
   10 #define SEP " "//设置分隔符 空格
   11 
   12 //保存打散之后的命令行字符串
   13 char *g_argv[SIZE];
   14 char cmd_line[NUM];   // 用来接收完整的命令行内容
   15  
   16 int main(void)
   17 {
   18   //0.命令行解释器,一定是一个常驻内存的进程,不退出
   19     while (1) {
   20         /* 1:显示提示信息 */
   21         printf("[amx@hecs-0-1  myshell] # ");
   22         fflush(stdout);
   23  
   24         /* 2:获取用户输入,输入的是各种指令和选项 :"ls -a -l "*/
   25         memset (
   26             cmd_line, 
   27             '\0', 
   28             sizeof(cmd_line) 
   29             );                                                                                                                                                   
   30        if( fgets(cmd_line, NUM, stdin)==NULL) {
   31           continue;                                     
   32        }/* 从键盘获取,标准输入,stdin 
   33             获取到 C 风格的字符串,默认添加 '\0' */
   34         cmd_line[strlen(cmd_line) - 1] = '\0';  //-1的目的是,最后一个字符的下标是长度减一,strlen不包括\n,\0这些
   35       // printf("%s\n", cmd_line);
   36       //3.命令行字符串解析:"ls -a -l "->"ls" "-a" "-l"
   37       g_argv[0]= strtok(cmd_line, SEP);//第一次调用,要传入原始字符串
   38       int index=1;
W> 39       while(g_argv[index++]=strtok(NULL,SEP));                                                                                                                   
   40         //for debug
   41        // for(index=0;g_argv[index];index++){
   42          // printf("g_argv[%d]:%s\n",index,g_argv[index]);
   43        // }
   44       // 4.TODO
   45       // 5.fork()
   46       pid_t id=fork();
   47         if(id==0){//子进程
   48             printf("下面功能让子进程执行的\n");
   49             execvp(g_argv[0],g_argv);//ls -a -l -i
   50             exit(1);
   51         }
   52         else{//父进程
   53           int status=0;                                                                                                                                          
   54           pid_t ret=waitpid(id,&status,0);                        
   55           if(ret>0) printf("exit code: %d\n",WEXITSTATUS(status));
   56          
   57         }
   58      
   59     }
   60 }

完美运行,你还可以输入vim进行编写,基本命令都可以!!!!!!!

但是但是但是:

当我推出到上一级时,他的目录并没有发生什么变化,为什么?

当你在cd的时候,无论什么命令都会执行exec,而执行exec命令只会影响当前的子进程的路径变化,而不是父进程路径变化,运行完子进程,子进程就退出了,所以我们还需要再做一些工作------如果是像cd这样的命令,我们不能创建子进程。

像这样,让父亲在自己去执行的命令我们叫做内置命令\内建命令-----本质就是shell中的函数调用。

4、内建命令:实现路径切换

代码实现:

运行结果:

在上层你看到的是个命令,但是在 shell 内部本质上是由父 shell 自己实现、调用的一个函数(并没有创建子进程),这种就是对应上上层的 内建命令。

内建命令表现是用用户层面的一条命令,本质就是 Shell 内部的一个函数,由父 Shell 自己执行,而不创建子进程。

还可以加颜色:

我们which一下可以发现颜色配色。

运行结果:

全部搞定!!!!!!!!

//除了 ll这个命令

我们看,因为ll本身就是ls的别名,如果想让它成立,我们还需要做一些判断:

至此,圆满结束!!!

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

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

相关文章

conda环境下Could not create share link解决方法

1 问题描述 在运行chatglm-6B项目时&#xff0c;运行python web_demo.py&#xff0c;出现如下错误&#xff1a; (chatglm) [rootlocalhost ChatGLM2-6B]# python web_demo.py Loading checkpoint shards: 100%|██████████████████████████████…

scratch给数据清单排序 2023年12月中国电子学会图形化编程 少儿编程 scratch编程等级考试四级真题和答案解析

目录 scratch给数据清单排序 一、题目要求 1、准备工作 2、功能实现 二、案例分析

性能优化-OpenMP基础教程(三)-Android上运行OpenMP

本文主要介绍如何在一个常规的Android手机上调试OpenMP程序&#xff0c;包括Android NDK的环境配置和使用JNI编写一个OpenMP程序运行在Android手机中。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#…

js——json对象相互转化——js基础积累

js——json对象相互转化——js基础积累 需求场景解决步骤1&#xff1a;定义一个变量接收此字段&#xff0c;方便处理解决步骤2&#xff1a; { 外面的双引号要去掉解决步骤3&#xff1a;使用正则去除参数中的\\解决步骤4&#xff1a;如果此参数必须以{开头&#xff0c;以}结尾解…

Java学校教务管理系统源码带微信小程序

运行环境&#xff1a;jdk8mysql5.7IntelliJ IDEAmaven 技术&#xff1a;springbootmybatislayuishirojquery 教务管理系统是一个基于网络的在线管理平台, 帮助学校管理教务系统&#xff0c;用一个帐号解决学校教务教学管理&#xff0c; 灵活的定制符合学校自己实际情况的教务系…

Eclipse下安装GDB

主要参考资料&#xff1a; 链接: https://blog.csdn.net/u013609041/article/details/18967837 目录 简介Eclipse中安装和配置GDB错误 简介 Eclipse是一款开发软件。 GDB是一个调试软件&#xff0c;但是GDB通常是运行在linux下的&#xff0c;无法直接在windows下运行&#xff…

RabbitMQ安装与应用

文章目录 1. RabbitMQ1.1. 同步通讯与异步通讯1.2. 异步通讯的优缺点1.3. 几种MQ的对比1.4. docker安装运行RabbitMQ 流程1.5. RabbitMQ的几个概念1.6. 五种模型1.6.1. 基本消息队列 1.7. 基本使用1.7.1. 1建立连接时会出现以下界面![在这里插入图片描述](https://img-blog.csd…

Redis第四讲——Redis的数据库结构、删除策略及淘汰策略

一、redis中的数据库 redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中。db数组的每项都是一个redis.h/redisDb结构&#xff0c;而每个redisDb结构就代表一个数据库。在初始化服务器时&#xff0c;程序会根据服务器状态的dbnum属性来决定应该创建多…

python+selenium爬虫笔记

本文只是做例子&#xff0c;具体网站路径麻烦你们换下&#xff0c;还有xpath路径也换下 一、安装所需要的组件&#xff08;此处采用谷歌&#xff09; 1、安装驱动 查看你的浏览器版本&#xff0c;去安装对应的版本 下载驱动 下载驱动路径 之前版本的 输入这个路径下载下来解压…

CTF-PWN-栈溢出-高级ROP-【SROP】

文章目录 linux信息处理2017 360春秋杯 smallest检查源码思路第一次要执行ret时的栈执行write函数时修改rsp到泄露的栈地址上去 输入/bin/sh并sigreturn调用系统调用回忆exp注意一个离离原上谱的地方 参考链接 SROP(Sigreturn Oriented Programming) 于 2014 年被 Vrije Univer…

系列十、Spring Cloud Gateway

一、Spring Cloud Gateway 1.1、概述 Spring Cloud全家桶中有个很重要的组件就是网关&#xff0c;在1.x版本中采用的是Zuul网关&#xff0c;但是在2.x版本中&#xff0c;由于Zuul的升级一直跳票&#xff0c;Spring Cloud最后自己研发了一个网关替代Zuul&#xff0c;即&#xf…

优雅实现微信小程序动态tabBar,根据不同用户角色显示不同底部导航——更新版(支持自由组合总数超过5个tabBar菜单)

背景 在开发小程序过程中&#xff0c;有个需求是&#xff0c;小程序底部的tabBar需要根据不同用户角色显示不同底部导航。此时就需要用到自定义底部导航 custom-tab-bar。 上次发文是组合显示4个底部tabBar导航&#xff0c;很多小伙伴评论说组合超过5个怎么办。他们的需求总数…

Android中的Intent

一.显式Intent 显示Intent是明确目标Activity的类名 1. 通过Intent(Context packageContext, Class<?> cls)构造方法 2.通过Intent的setComponent()方法 3.通过Intent的setClass/setClassName方法 通过Intent(Context packageContext, Class<?> cls)构造方法 通…

JVM之对象创建

对象创建的流程 1.类加载检查 虚拟机遇到一条new指令时&#xff0c;首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用&#xff0c;并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有&#xff0c;那必须先执行相应的类加载过程。new指令对…

Callback Hook

一、Callback Hook 函数名&#xff1a;useCallback 用于得到一个固定引用值的函数&#xff0c;通常用它进行性能优化。 useCallback: 该函数只需要传入两个参数&#xff1a;一个回调函数和一个依赖数组即可。 1.函数&#xff0c;useCallback会固定该函数的引用&#xff0c;…

【Rust日报】Piccolo - 用纯Rust实现的无栈Lua虚拟机

Piccolo - 用纯Rust实现的无栈Lua虚拟机 Piccolo&#xff0c;原名luster&#xff0c;在经过数年的中断后&#xff0c;于2023年4月悄然恢复了开发。曾经开发过 rlua 的 kyren&#xff0c;在底层 gc-arena crate 取得突破后&#xff0c;回到了 piccolo 项目。这两个项目现在已经&…

Python:界面开发,wx入门篇

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/3Yb_YAKiMte_f5HanetXiA 本文大概 3617 个字&#xff0c;阅读需花 10 分钟 内容不多&#xff0c;但也花了一些精力 如要交流&#xff0c;欢迎评…

极速 JavaScript 打包器:esbuild

文章目录 引言什么是esbuild&#xff1f;esbuild的特点esbuild如何实现如此出色的性能&#xff1f;esbuild缺点基本配置入口文件输出文件模块格式targetplatformexternalbanner和footer 高级配置插件系统自定义插件压缩代码调试代码 结论&#x1f636; 写在结尾 引言 esbuild是…

leetcode:724. 寻找数组的中心下标

一、题目 二、函数原型 int pivotIndex(int* nums, int numsSize) 三、思路 首先要理解正确中心下标&#xff0c;中心下标左侧元素之和等于右侧元素之和&#xff0c;比较时是不包含中心下标所指元素的。 先将数组和求出来记为sum&#xff0c;再遍历数组&#xff0c;遍历到…

快速批量运行命令

Ansible 是 redhat 提供的自动化运维工具&#xff0c;它是 Python编写&#xff0c;可以通过 pip 安装。 pip install ansible 它通过任务(task)、角色(role)、剧本(playbook) 组织工作项目&#xff0c;适用于批量化系统配置、软件部署等需要复杂操作的工作。 但对于批量运行命…