【进程控制⑦】:制作简易shell理解shell运行原理

【进程控制⑦】:制作简易shell&&理解shell运行原理

  • 一.交互问题,获取命令行
  • 二.字串分割问题,解析命令行
  • 三.指令的判断
  • 四.普通命令的执行
  • 五.shell原理本质

一.交互问题,获取命令行

在这里插入图片描述
shell刚启动时就会出现一行命令行,这一行命令行分别表示的用户是谁,主机是谁,当前目录在哪等。
而我们如果想要制作一个shell,肯定也需要输出这些信息,那这些信息从哪里获得呢?
我们直接从环境变量里获取:
在这里插入图片描述
所以我们可以利用getenv系统接口获取环境变量里的内容:
分别获取用户,主机和当前目录信息
然后我们按照shell刚启动时的方式输出这些信息:
并且可以输入命令行:注意我们要用fgets来输入命令行,不能用scanf输入,因为scanf遇到空格就会阻塞。我们要完整的输出一行。

    1 
    2 #include <stdio.h>
    3 #include <stdlib.h>
    4 #include <assert.h>
    5 #include <string.h>
    6 #include <unistd.h>
    7 #include <sys/wait.h>
    8 #include <sys/types.h>
    9 #define LEFT  "["
   10 #define RIGHT "]"
   11 #define LABLE "#"
   12 #define LINE 1024
   19 char pwd[LINE];//存储当前目录
   20 char commandline[LINE];//用户输入的命令行
   27 const char* getusername()
   28 {
   29    return getenv("USER");
   30 }
   31 
   32 const char* gethostname()
   33 {                                                                                                            
   34   return getenv("HOSTNAME");
   35 }
   36 void  getpwd()
   37 {
   38    //直接调用系统接口获取当前目录,并写入字串里
   39    getcwd(pwd,sizeof(pwd));
   40 }
//1.获取用户输入的命令
   43 void interact(char* cline,int size)
   44 {
   45   getpwd();//更新一下当前的目录并放入到pwd字符串里
E> 46   printf(LEFT"%s@%s%s"RIGHT""LABLE" ",getusername(),gethostname(),pwd);
   47  char*s= fgets(cline,size,stdin); //因为后面不会再用到s,操作系统会报错,所以下面处理一下表示用过
   48  assert(s);//断言声明一下,当s为null时就报错
   49  (void)s;//表示用过
   50 
   51  cline[strlen(cline)-1]='\0';
   52  //因为最后无论如何都要按enter,所以必定会有\换行,abcd\n\0但是我们并不想要这个换行,所以将这个换行替换成\0
   53 }
int main()
  121 {
  122   //shell本质上是一个死循环,一直在使用
  123   int quit=0;
  124   while(!quit)
  125   {
  126 
  127    interact(commandline,sizeof(commandline));
  136    return 0;
  137
  138    }

这样当我们运行程序时,就会输出像shell刚启动时的样子:
在这里插入图片描述
这样我们就可以输入命令行了!当我们输入命令行后,我们知道shell会将这些命令行分割成一个一个字串然后执行。所以我们也需要将命令行分割出来,然后解析命令行!分析命令行要干什么

二.字串分割问题,解析命令行

我们分割的字串都放在哪里呢?它们的地址都在哪呢?
这时我们是不是就想当一个表:叫命令行参数表!
其实分割的命令行参数都放在命令行参数表里了,命令行参数表是一个指针数组,里面的都是存在各个字串的地址。
而这里我们定义的命令行参数表作为一个输出型参数,将我们命令行分割的字串带出来。并且计算分割的字串的个数是多少。

   13 #define ARGV_SIZE 32
   17 char *argv[ARGV_SIZE];//命令行参数表,作为输出型参数,将分割的命令行子串保存,保存每个字串的地址

 //2.子串分割问题,解析命令行
   55 int splitstring(char _commandline[],char *argv[])
   56 {
   56      int i=0;//用来计算切割的字符个数
   57
   57     //利用strtok来分割字串串,strtok的用法是第一次传字符串参数,以后就不用传,设为NULL
   58     argv[i++]=strtok(_commandline,DELIM);
W> 59     while(argv[i++]=strtok(NULL,DELIM));
   60 
   61     return i-1;//最后还会加1所以需要减1.
   62       
   63 }
int main()
  121 {
  122   //shell本质上是一个死循环,一直在使用
  123   int quit=0;
  124   while(!quit)
  125   {
  126 
  127  interact(commandline,sizeof(commandline));
  128  int argc=splitstring(commandline,argv);
  129  if(argc==0)continue;//表明是空串
  136    return 0;
  137   }


三.指令的判断

第三步可以先跳过看第四步,然后再回来看第三步。
为什么要对命令判断呢?判断什么呢?直接执行不行吗?
不行!因为shell中存在内建命令和普通命令之分,普通命令就是通过子进程程序替换来执行,而内建命令必须是由父进程来执行,不能是子进程来执行。为什么呢?
比如cd命令,cd命令是进入某个目录中,如果让子进程执行,当打印当前目录时,进程的当前目录并没有发生改变。因为进程进入了cd要进入的目录,可是这跟父进程有什么关系呢?父进程就不会进入。
所以当命令是cd时,就需要父进程来执行而不能创建子进程来执行。
还有比如export命令,echo命令等都是内建命令,需要父进程执行。
export命令是创建环境变量,必须是由父进程执行,子进程执行父进程就获取不到,而echo命令存在这样的场景:echo $? 会输出最近进程的退出码。这个应该是由父进程来执行的。还有当输入"内容" 输出的应该是内容而不是"内容".还有输出环境变量时应该会输出环境变量对于的内容。这些单纯的程序替换是做不到的。

 int buildcommand(char*_argv[],int _argc)
   99 {
  100 
  101  if(_argc==2&&strcmp(_argv[0],"cd")==0)
  102  {
  103     chdir(argv[1]);//直接调用系统接口,跳到指定目录
  104     //跳到指定目录后,环境变量里的PWD也需要更改到当前目录
  105     getpwd();
  106     sprintf(getenv("PWD"),"%s",pwd);//获取到PWD环境变量的内容并将pwd写入到PWD里
  107     return 1;//执行完内建命令后,普通命令就跳过
  108     //对于内建命令,本质就是shell的内部的一个函数
  109  }
  else if(_argc == 2 && strcmp(_argv[0], "export") == 0)//export直接利用putenv接口将环境变量创建
  100     {
   96         strcpy(myenv, _argv[1]);
   97         putenv(myenv);
   98         return 1;
   99     }
  100     else if(_argc == 2 && strcmp(_argv[0], "echo") == 0)//输出最近进程的退出码
  100     {
  101         if(strcmp(_argv[1], "$?") == 0)
  102         {
  103             printf("%d\n", lastcode);//将子进程的退出码输出
  104             lastcode=0;
  105         }
  106         else if(*_argv[1] == '$')//输出环境变量
             {
  107             char *val = getenv(_argv[1]+1);
  108             if(val) printf("%s\n", val);
  109         }
  110         else//单纯的打印里面的内容
              {
  111             printf("%s\n", _argv[1]);
  112         }
  113 
  114         return 1;
  115     }
  116       return 0
  int main()
  127 {
  128     while(!quit){
  129        
  131         interact(commandline, sizeof(commandline));
  132 
  133         
  135         int argc = splitstring(commandline, argv);
  136         if(argc == 0) continue;
  137 
  138       
  141         //内键命令,本质就是一个shell内部的一个函数
  142         int n = buildCommand(argv, argc);
  143 
  144         // 5. 普通命令的执行
  145         if(!n) NormalExcute(argv);
  146     }
  147     return 0;
  148 }

四.普通命令的执行

分割完命令行参数,并存放在命令行参数表里后,我们就可以执行命令了!如何执行呢?通过程序替换来执行!也就是我们可以直接可以替换成库里已经提供的可执行程序。比如我们输入ls命令,那么我们就可以直接替换成系统里已经提供的ls命令程序。
对于命令的执行,我们都是通过创建子进程来执行,也就是让子进程进行程序替换。然后父进程等待子进程。

   18 int lastcode = 0;
   65 //4.普通命令的执行--->通过创建子进程来执行,子进程执行,退出,父进程等待
   66 void normalexcute(char *_argv[])                                                                             
   67 {
   68 
   69  //对于cd命令,执行命令的是子进程关父进程什么事,所以pwd显示的还是父进程当前目录,所以cd应该是父进程执行,而>      不是子进程执行,其实cd是内健命令
   70 //父进程创建子进程
   71  pid_t id =fork();
   72  if(id<0)
   73  {
   74    perror("fork错误");
   75    return;
   76  }
   77  else if(id==0)//子进程
   78  {
   79      //子进程如何执行普通命令呢?通过进程替换!exec* 借助库函数里的,需要带p的v的
   80     execvp(_argv[0],_argv);
   81     //不会返回,如果返回了那么就说明进程替换错误
   82     exit(EXIT_CODE);
   83      
   84  }
   85  else//父进程获取子进程的退出结果 
   86  {
   87  
   88    int status=0;
   89    pid_t ret=waitpid(id,&status,0);
   90    if(ret==id)
   91    {
   92       lastcode=WEXITSTATUS(status);
   93    }
   94  }
   95 
   96 }
   97 
  122   //shell本质上是一个死循环,一直在使用
  123   int quit=0;
  124   while(!quit)
  125   {
  126 
  127  interact(commandline,sizeof(commandline));
  128  int argc=splitstring(commandline,argv);
  129  if(argc==0)continue;//表明是空串
  130  
  134   normalexcute(argv);//命令执行
  135   
  136  return 0;
  137 }

五.shell原理本质

shell本质就是一个进程,当Xshell程序启动时,就创建了一个进程,这个进程本质是一个死循环。进程首先会输出当前使用者是谁,主机是谁,当前目录在哪。然后当你输入命令时,shell进程会将这个命令行分割成各个字串并存放在命令行参数表里。然后shell就会对这个参数表进行解析,当是普通命令时,就创建子进程进行程序替换执行,当是内建命令时,就是shell进程自己执行。对于环境变量,shell这个进程的环境变量是操作系统就分配好了,创建时就从一个配置文件中直接获取到。然后子进程的环境变量就从父进程继承下去。以上就是shell运行的原理本质!

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

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

相关文章

01_stable_diffusion_introduction_CN

stable_diffusion 配置 !pip install -Uq diffusers ftfy accelerate# Installing transformers from source for now since we need the latest version for Depth2Img: !pip install -Uq githttps://github.com/huggingface/transformers import torch import requests fro…

Logstash学习

1、什么是logstash logstash是一个数据抽取工具&#xff0c;将数据从一个地方转移到另一个地方。如hadoop生态圈的sqoop等。下载地址: https://www.elastic.co/cn/downloads/logstash logstash之所以功能强大和流行&#xff0c;还与其丰富的过滤器插件是分不开的&#xff0c;过…

搭建个人hMailServer邮件服务实现远程发送邮件

文章目录 前言1. 安装hMailServer2. 设置hMailServer3. 客户端安装添加账号4. 测试发送邮件5. 安装cpolar6. 创建公网地址7. 测试远程发送邮件8. 固定连接公网地址9. 测试固定远程地址发送邮件 前言 hMailServer 是一个邮件服务器,通过它我们可以搭建自己的邮件服务,通过cpola…

python连接clickhouse (CK)

Author: tkhywang 2810248865qq.com Date: 2023-11-01 11:28:58 LastEditors: tkhywang 2810248865qq.com LastEditTime: 2023-11-01 11:36:25 FilePath: \PythonProject02\Python读取clickhouse2 数据库数据.py Description: 这是默认设置,请设置customMade, 打开koroFileHead…

tcp/ip该来的还是得来

1. TCP/IP、Http、Socket的区别 \qquad 区别是&#xff1a;TCP/IP即传输控制/网络协议&#xff0c;也叫作网络通讯协议&#xff0c;它是在网络的使用中的最基本的通信协议。Http是一个简单的请求-响应协议&#xff0c;它通常运行在TCP之上。Socket是对网络中不同主机上的应用进…

react-hook-form。 useFieldArray Controller 必填,报错自动获取较多疑问记录

背景 动态多个数据Controller包裹时候&#xff0c;原生html标签input可以add时候自动获取焦点&#xff0c;聚焦到最近不符合要求的元素上面 matiral的TextField同样可以可是x-date-pickers/DatePicker不可以❌ 是什么原因呢&#xff0c;内部提供foucs&#xff1f;&#xff1f;属…

这才是当今生成式人工智能的根本性问题!

原创 | 文 BFT机器人 01 引言 近年来&#xff0c;生成式人工智能产品层出不穷&#xff0c;ChatGPT火爆出圈后&#xff0c;百度、谷歌等科技大佬争相研究生成式人工智能产品&#xff0c;将该技术的普及程度提升到了一个新的水平。然而&#xff0c;生成式人工智能的运营需要高昂…

【Redis】入门篇--安装以及常用命令

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于Redis的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.Redis是什么 二. Redis有什么优势 三…

谷歌浏览器解决跨域问题配置记录

在访问时出现has been blocked by CORS policy: Responspreflight request doesn’t pass access control checlAccess-Control-A1low-Origin" header is present onrequested resource. 出现跨域问题 1.先关闭浏览器 2.创建一个目录&#xff0c;文件夹记住路径 3.点击谷…

强化学习的动态规划

一、动态规划 动态规划&#xff08;DP&#xff09;一词指的是一系列算法&#xff0c;这些算法可用于在给定环境的完美模型作为马尔可夫决策过程&#xff08;MDP&#xff09;的情况下计算最优策略。经典的DP算法在强化学习中具有有限的实用性&#xff0c;既因为其对完美模型的假…

开源播放器GSYVideoPlayer的简单介绍及播放rtsp流的优化

开源播放器GSYVideoPlayer的简单介绍及播放rtsp流的优化 前言一、GSYVideoPlayer&#x1f525;&#x1f525;&#x1f525;是什么&#xff1f;二、简单使用1.First、在project下的build.gradle添加2.按需导入3. 常用代码 rtsp流的优化大功告成 总结 前言 本文介绍&#xff0c;…

Uni-App 快捷登录

uniapp 实现一键登录前置条件: 开通uniCloud, 开通一键登录功能参考的文档 : 官网 - 一键登录uniapp指南 : https://uniapp.dcloud.net.cn/univerify.html#%E6%A6%82%E8%BF%B0 官网 - 一键登录开通指南 : https://ask.dcloud.net.cn/article/37965 官网 - unicloud使用指南 htt…

Greenplum管理和监控工具-gpcc-web介绍

Greenplum管理和监控工具-gpcc-web介绍 1. gpcc-web简介 ​ gpcc&#xff08;Greenplum Command Center&#xff09;的Web用户界面是一个强大的工具&#xff0c;它可以帮助用户管理Greenplum数据库集群&#xff0c;提高效率&#xff0c;优化性能&#xff0c;并确保数据的安全…

应用场景由点及面,大模型在银行业落地的方法|案例研究

自2022年11月面世以来&#xff0c;ChatGPT已经吸引了全球范围内的广泛关注。其底层技术大模型&#xff0c;也获得了银行业自上而下所有人员前所未有的关注度。 01 相较于传统AI小模型&#xff0c;大模型具有以下三大核心优势&#xff1a;效率提升&#xff0c;个性化输出和交互…

二维码智慧门牌管理系统升级解决方案:让门牌安装任务更加智能化

文章目录 前言一、任务地图和任务领取二、贴牌作业和提交作业三、优势与效益四、自媒体平台的吸引力 前言 随着科技的不断发展&#xff0c;智能化管理在各个领域的应用越来越广泛。在门牌安装领域&#xff0c;二维码智慧门牌管理系统已经成为了一种新的升级解决方案&#xff0…

什么样的耳机适合跑步?适合跑步佩戴的无线耳机推荐

​无论是在烈日炎炎的夏天&#xff0c;还是在寒风刺骨的冬天里健身运动&#xff0c;只要打开音乐就能沉浸其中。运动耳机不仅佩戴稳固舒适&#xff0c;还能提供高品质音质表现。无论在哪里&#xff0c;无论何时&#xff0c;只要打开音乐&#xff0c;你就可以找到你的节奏&#…

日本移动支付Merpay QA团队的自动化现状

Merpay是日本最大的网购平台之一Mercari的无现金支付系统。Merpay 的主要功能是让用户在 Mercari的网站上购物&#xff0c;也可以在日本的许多实体店和餐厅使用它&#xff0c;也可以理解为日本的“支付宝”。以下为Merpay QA 团队在自动化方面的一些思考&#xff1a; 这几年&am…

AI、万圣节与聊斋;用AI写甜蜜恋爱小暖文;AGI新趋势与机会洞察;Meta官方Llama 2入门指南 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f440; 时代杂志评选2023年度最佳发明&#xff0c;AI赛道入选名单 https://time.com/collection/best-inventions-2023 10 月 24 日&#xff…

【排序算法】 计数排序(非比较排序)详解!了解哈希思想!

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; 算法—排序篇 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言&#x1f324;️计数排序的概念☁️什么是计数排序&#xff1f;☁️计数排序思想⭐绝对…

安全第一!速卖通测评补单稳定的系统注意事项大盘点

对新卖家而言&#xff0c;测评并非可耻之事&#xff0c;反而是无法起步、耗费自身时间才是真正的可耻。由于速卖通新店几乎无法获得任何活动的支持&#xff0c;流量也基本没有&#xff0c;因此要在90天内达成60单的业绩对于许多卖家来说都是一项挑战。因此&#xff0c;通过快速…