【linux】实现shell

news2024/11/20 14:38:19

自我名言只有努力,才能追逐梦想,只有努力,才不会欺骗自己。在这里插入图片描述
喜欢的点赞,收藏,关注一下把!在这里插入图片描述
如果发现内容有不对的地方欢迎在评论区批评指正,这是对我最大的鼓励!!!

前面学过了进程创建/终止/等待/替换。现在根据所学的内容实现一个简易版的shell。

//头文件
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<sys/types.h>  
  5 #include<sys/wait.h>  
  6 #include<string.h>

1.提示符

这是我们在命令行上首先出现的内容。
在这里插入图片描述
注意在命令行输入指令,是在同一行的。

int main()  
{
     //这里也可以使用环境变量来获取这些内容
     printf("用户名@主机名 当前路径#"); 
	 //这里printf后面不能带\n,但是要把内容打印到显示器,因此必须刷新缓冲区。
     fflush(stdout);                                                                                                                                                                                                                                                                                                        
     return 0;                                                                                                                                                
 }  

2.命令行参数

在这里插入图片描述

前面说过ls -a -l是命令行参数,是一个整体的字符串,分割层一个个字符串传到main函数中。

在这里插入图片描述
因此,我们需要把输入的字符串也要分割成一个个字符串。

2.1输入指令

    7 #define NUM 1024
    8 #define OPT_NUM 64
    9 //全局变量
   10 char lineCommand[NUM];//存放输入的指令字符串
   11 char* myargv[OPT_NUM];//指针数组,存放分割的一个个字符串
   12 

这里使用fgets函数,把输入流放在指定数组。

在这里插入图片描述

    9 #define NUM 1024
   10 #define OPT_NUM 64
   11 
   12 char lineCommand[NUM];
   13 char* myargv[OPT_NUM];//指针数组,存放分割的一个个字符串
   14 
   15 int main()
   16 {
   17     //这里也可以使用环境变量来获取这些内容
   18     printf("用户名@主机名 当前路径# ");
   19     fflush(stdout);
   20     //"ls -a -l"-----> "ls" "-a" "-l"
   21     //这里减1,是为了极端情况下放\0
   22     char* s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);
   23     assert(s);
   24     //测试一下                                                                                                                                                 
   25     printf("test:%s\n",lineCommand);
   26 
   27     return 0;
   28 }


在这里插入图片描述

发现这里空了一格。这是因为我们输入指令的时候,最后一次肯定敲的是\n,因此这里我们消除一下\n。

    9 #define NUM 1024
   10 #define OPT_NUM 64
   11 
   12 char lineCommand[NUM];
   13 char* myargv[OPT_NUM];//指针数组,存放分割的一个个字符串
   14 
   15 int main()
   16 {
   17     //这里也可以使用环境变量来获取这些内容
   18     printf("用户名@主机名 当前路径# ");
   19     fflush(stdout);
   20     //"ls -a -l"-----> "ls" "-a" "-l"
   21     //这里减1,是为了极端情况下放\0
W> 22     char* s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);
   23     assert(s);
   24     //清除最后一个\n  abc\n                                                                                                                                    
   25     lineCommand[strlen(lineCommand)-1]=0;                                                                                                    
   26     //测试一下                                                                                                                               
   27     printf("test:%s\n",lineCommand);                                                                                                         
   28                                                                                                                                              
   29     return 0;                                                                                                                                
   30 }                  

在这里插入图片描述

2.2分割指令

在C语言的时候,学过一个分割字符串的函数,strtok

在这里插入图片描述

   28     //这里以空格为分隔符
   29     myargv[0]=strtok(lineCommand," ");
   30     //分割的是同一个字符串,下一次第一个参数就置为NULL就可以了
   31     // myargv[1]=strtok(NULL,"");
   32     //这里需要实现循环,注意strtok到字符串结束,会返回NULL,myargv[end]=NULL                                                                                    
   33     int i=1;
   34     while(myargv[i++]=strtok(NULL," "));

   36 //条件编译,测试分割是否成功
   37 #ifdef DEBUG
   38     for(int i=0;myargv[i];++i)
   39     {
   40         printf("myargv[%d]:%s\n",i,myargv[i]);
   41     }
   42 #endif    
  1 myshell:myshell.c
  2     gcc -o $@ $^ -std=c99 -DDEBUG //定义宏  不需要就前面加个#注释掉                                                                                                                               
  3 
  4 .PHONY:clean
  5 clean:
  6     rm -f myshell

在这里插入图片描述

3.创建子进程执行指令

前面我们学了6个替换函数。这里我们选择execvp最合适。

在这里插入图片描述

   44     //创建子进程
   45     pid_t id = fork();
   46     assert(id != -1);
   47     if(id == 0)
   48     {
   49         //子进程
   50         execvp(myargv[0],myargv);    
   51     }
   52     //父进程
   53     //这里先不关心退出码
   54     waitpid(id,NULL,0);

在这里插入图片描述

但是这里只能实现一次,因此,需要把整体循环起来。

	1#include<stdio.h>
    2 #include<stdlib.h>
    3 #include<unistd.h>
    4 #include<assert.h>
    5 #include<sys/types.h>
    6 #include<sys/wait.h>
    7 #include<string.h>
    8 
    9 #define NUM 1024
   10 #define OPT_NUM 64
   11 
   12 char lineCommand[NUM];
   13 char* myargv[OPT_NUM];//指针数组,存放分割的一个个字符串
   14 
   15 int main()
   16 {
   17     while(1)
   18     {
   19          //这里也可以使用环境变量来获取这些内容
   20          printf("用户名@主机名 当前路径# "); 
   21          fflush(stdout);
   22          //"ls -a -l"-----> "ls" "-a" "-l"
   23          //这里减1,是为了极端情况下放\0
   24          char* s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);
   25          assert(s);
   26          //清除最后一个\n  abc\n
   27          lineCommand[strlen(lineCommand)-1]=0;
   28          //测试一下
   29          //printf("test:%s\n",lineCommand);
   30          //这里以空格为分隔符
   31          myargv[0]=strtok(lineCommand," ");
   32          //分割的是同一个字符串,下一次第一个参数就置为NULL就可以了
   33          // myargv[1]=strtok(NULL,"");                                                                                                                         
   34          //这里需要实现循环,注意strtok到字符串结束,会返回NULL,myargv[end]=NULL
   35          int i=1;
   36          while(myargv[i++]=strtok(NULL," "));
   37 
   38 //测试分割是否成功
   39#ifdef DEBUG
   40          for(int i=0;myargv[i];++i)
   41          {
   42              printf("myargv[%d]:%s\n",i,myargv[i]);
   43          }
   44 #endif
   45 
   46          //创建子进程
   47          pid_t id = fork();
   48          assert(id != -1);
   49          if(id == 0)
   50          {
   51              //子进程
   52              execvp(myargv[0],myargv);    
   53          }
   54          //父进程
   55          //这里先不关心退出码
   56          waitpid(id,NULL,0);
   57     }
   58     return 0;
   59 }

在这里插入图片描述

4.三个细节

4.1ls可执行程序问题

在这里插入图片描述
自己实现的shell,可执行程序没有颜色,

   36          //这里对ls,特殊处理                                                                                                                                
   37          if(myargv[0] != NULL && strcmp(myargv[0],"ls") == 0)
   38          {
   39              myargv[i++]=(char*)"--color=auto";                                                                                                                
   40          }  

在这里插入图片描述

4.2切换路径问题

在这里插入图片描述
我们发现,当我切换到上层路径时,发现当前路径没有变。这是为什么呢?

什么是当前路径?
在前面我们学过一个查看进程的命令ls /proc

在这里插入图片描述
exe----> 是当前进程执行的是磁盘路径下哪一个程序!
cmd----> 是当前进程的工作目录

什么是当前路径,默认是你在那个路径下把程序跑起来。本质就是当前进程的工作目录。

当前路径知道了,那为什么自己写的shell,cd的时候,路径没有变化呢?

子进程也有自己的工作目录,默认和父进程一样。fork之后,子进程执行cd命令,更改的是子进程的目录!子进程执行完毕之后,被回收了。继续使用的是父进程。然后再创建子进程。默认和父进程工作目录一样,这时执行pwd命令,所以当前目录没有变。

如果就想更改当前工作目录,系统提供chdir函数。
在这里插入图片描述

   1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 
  5 int main()
  6 {
  7     //想让工作目录是谁,就把谁的路径传过来
  8     chdir("/home/wdl");
  9     while(1)
 10     {
 11         printf("我是一个进程,我的id是:%d\n",getpid());
 12         sleep(1);
 13     }
 14                                                                                                                                                                  
 15     return 0;
 16 } 

在这里插入图片描述
修改一下自己的代码。

				//如果是cd命令,不需要创建子进程,让shell自己执行对应的命令,本质就是执行系统接口
        		//像这种不需要让我们的子进程来执行,而是让shell自己执行的命令 --- 内建/内置命令
   42          if(myargv[0] != NULL && strcmp(myargv[0],"cd") == 0)                                                                                                
   43          {                                                                                                                                                   
   44              if(myargv[1] != NULL)
   45              {
   46                  chdir(myargv[1]);
   47                  continue;                                                                                                                                     
   48              }                                                                                                                                               
   49          }   

在这里插入图片描述

4.3进程退出码

echo $?  //记录最近一个程序的进程退出码

在这里插入图片描述
具体实现

	//两个全局变量记录子进程退出信号,退出码
   12 int lastCode=0;
   13 int lastsig=0;
				//特殊处理
   57          if(myargv[0] != NULL && strcmp(myargv[0],"echo") == 0)                                                                                            
   58          {                                                                                                                                                 
   59             if(strcmp(myargv[1],"$?") == 0)                                                                                                                
   60             {                                                                                                                                              
   61                 printf("%d,%d\n",lastCode,lastsig);
   62             }
   63             else
   64             {
   65                 printf("%s\n",myargv[1]);
   66             }
   67             continue;                                                                                                                                          
   68          }    
		       
   78          //创建子进程
   79          pid_t id = fork();
   80          assert(id != -1);
   81          if(id == 0)
   82          {
   83              //子进程
   84              execvp(myargv[0],myargv);
   85              exit(1);
   86          }
   87          //父进程
   88          int status=0;
   89          pid_t ret= waitpid(id,NULL,0);
   90          assert(ret>0);
   				//记录退出信号,退出码
   91          lastCode=(status>>8)&0xFF;
   92          lastsig=status&0x7F;
   93     return 0;

在这里插入图片描述

5.myshell完整代码

#include<stdio.h>
    2 #include<stdlib.h>
    3 #include<unistd.h>
    4 #include<assert.h>
    5 #include<sys/types.h>
    6 #include<sys/wait.h>
    7 #include<string.h>
    8 
    9 #define NUM 1024
   10 #define OPT_NUM 64
   11 
   12 int lastCode=0;
   13 int lastsig=0;
   14 
   15 char lineCommand[NUM];
   16 char* myargv[OPT_NUM];//指针数组,存放分割的一个个字符串
   17 
   18 int main()
   19 {
   20     while(1)
   21     {
   22          //这里也可以使用环境变量来获取这些内容
   23          printf("用户名@主机名 当前路径# "); 
   24          fflush(stdout);
   25          //"ls -a -l"-----> "ls" "-a" "-l"
   26          //这里减1,是为了极端情况下放\0
W> 27          char* s=fgets(lineCommand,sizeof(lineCommand)-1,stdin);
   28          assert(s);
   29          //清除最后一个\n  abc\n
   30          lineCommand[strlen(lineCommand)-1]=0;
   31          //测试一下
   32          //printf("test:%s\n",lineCommand);
   33          //这里以空格为分隔符                                                                                                                                  
   34          myargv[0]=strtok(lineCommand," ");
   35          //分割的是同一个字符串,下一次第一个参数就置为NULL就可以了
   36          // myargv[1]=strtok(NULL,"");
   37          //这里需要实现循环,注意strtok到字符串结束,会返回NULL,myargv[end]=NULL
   38          int i=1;
   39			//这里对ls,特殊处理
   40          if(myargv[0] != NULL && strcmp(myargv[0],"ls") == 0)
   41          {
   42              myargv[i++]=(char*)"--color=auto";
   43          }
   44         
W> 45          while(myargv[i++]=strtok(NULL," "));
   46         //如果是cd命令,不需要创建子进程,让shell自己执行对应的命令,本质就是执行系统接口
   47         //像这种不需要让我们的子进程来执行,而是让shell自己执行的命令 --- 内建/内置命令
   48          if(myargv[0] != NULL && strcmp(myargv[0],"cd") == 0)                                                                                                  
   49          {
   50              if(myargv[1] != NULL)
   51              {
   52                  chdir(myargv[1]);
   53                  continue;
   54              }
   55          }
   56 
   57          if(myargv[0] != NULL && strcmp(myargv[0],"echo") == 0)
   58          {
   59             if(strcmp(myargv[1],"$?") == 0)
   60             {
   61                 printf("%d,%d\n",lastCode,lastsig);
   62             }
   63             else
   64             {
   65                 printf("%s\n",myargv[1]);
   66             }
   67             continue;
   68          }
   69          
   70 //测试分割是否成功
   71 #ifdef DEBUG
   72          for(int i=0;myargv[i];++i)
   73          {
   74              printf("myargv[%d]:%s\n",i,myargv[i]);
   75          }
   76 #endif
   77 
   78          //创建子进程
   79          pid_t id = fork();
   80          assert(id != -1);
   81          if(id == 0)
   82          {
   83              //子进程
   84              execvp(myargv[0],myargv);
   85              exit(1);
   86          }
   87          //父进程
   88          int status=0;
W> 89          pid_t ret= waitpid(id,NULL,0);
   90          assert(ret>0);
   91          lastCode=(status>>8)&0xFF;
   92          lastsig=status&0x7F;
   93          return 0;
   94     }
   95 }

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

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

相关文章

对于现代互联网企业来说Python数据分析有什么用?

我们每一个人&#xff0c;每天无时无刻都在生产数据&#xff0c;一分钟内&#xff0c;微博上新发的数据量超过10万&#xff0c;b站的视频播放量超过600万……这些庞大的数字&#xff0c;意味着什么&#xff1f;意味着每天需要大量的人员要对这些数据进行分析&#xff0c;筛选有…

ssm+vue的教室信息管理系统(有报告)。Javaee项目,ssm vue前后端分离项目。

演示视频&#xff1a; ssmvue的教室信息管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;ssm vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

OpenHarmony应用开发涉及的主要因素与UX设计规范

一、OpenHarmony应用开发涉及的主要因素 二、OpenHarmony应用开发UX设计规范 UX设计规范的主要内容与部分图标示例 2.OpenHarmony应用设计原则 设计原则&#xff0c;当为多种不同的设备开发应用时&#xff0c;有如下设计原则&#xff1a; 差异性&#xff0c;充分了解所要支…

LinearLayout里子view点击,其他空白间隙处禁止点击

LinearLayout里子view点击&#xff0c;其他空白间隙处禁止点击 经过不断摸索终于实现了。 像头条里黄色区域禁止点击实现。 可以通过在父 LinearLayout 上设置 android:clickable"true" 属性来实现&#xff0c;然后在子 View 上设置 android:clickable"false&…

1688-阿里巴巴批发网(获取商品的名称,价格,图片)

1688 item_get-获得1688商品详情 为了进行电商平台 的API开发&#xff0c;首先我们需要做下面几件事情。 1&#xff09;开发者注册一个账号 2&#xff09;然后为每个1688 应用注册一个应用程序键&#xff08;App Key) 。 3&#xff09;下载1688 API的SDK并掌握基本的API基础…

红海云签约中材叶片,科技引领风电叶片行业人力资源数字化转型

中材科技风电叶片股份有限公司&#xff08;以下简称“中材叶片”&#xff09;隶属于世界500强央企集团——中国建材集团&#xff0c;是专业的风电叶片设计、研发、制造和服务提供商&#xff0c;致力于打造最为客户尊重与员工、股东信赖的具有全球竞争力的世界一流风电叶片企业。…

Spring Cloud阿里的nacos注册中心的使用 Feign远程调用 nacos配置中心的简单使用

原文档 注册中心 https://github.com/alibaba/spring-cloud-alibaba/blob/2022.x/spring-cloud-alibaba-examples/nacos-example/nacos-discovery-example/readme-zh.md 配置中心 https://github.com/alibaba/spring-cloud-alibaba/blob/2022.x/spring-cloud-alibaba-example…

XSAN数据恢复-XSAN迁移数据过程中误格式化存储系统的数据恢复案例

XSAN数据恢复环境&#xff1a; 昆腾存储&#xff0c;MAC OS操作系统&#xff0c;划分了9个数据卷&#xff08;1个META信息卷&#xff0c;8个DATA信息卷&#xff09;&#xff0c;存放视频类数据&#xff0c;MXF、MOV等格式文件。 XSAN故障&分析&#xff1a; 将存储空间从XS…

力扣337.打家劫舍3(树形dp)

题目描述&#xff1a; 小偷又发现了一个新的可行窃的地区。这个地区只有一个入口&#xff0c;我们称之为 root 。 除了 root 之外&#xff0c;每栋房子有且只有一个“父“房子与之相连。一番侦察之后&#xff0c;聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树…

Sip双按键对讲终端 医护对讲终端

Sip双按键对讲终端 医护对讲终端 1、前言 SIP对讲终端SIP-6002D双按键是一款采用了ARMDSP架构&#xff1b;配置了麦克风输入和扬声器输出&#xff0c;SIP-6002D带两路寻呼按键&#xff0c;可实现SIP对讲功能&#xff0c;作为SIP对讲的终端&#xff0c;主要用于银行调度对讲、部…

华为---STP协议简介(一)

生成树协议简介 什么是生成树协议 STP&#xff08;Spanning Tree Protocol&#xff09;是一种由交换机运行的、用来解决交换网络中环路问题的数据链路层协议。为提高网络可靠性&#xff0c;交换网络中通常会使用冗余链路&#xff0c;但是冗余链路会给交换网络带来环路风险&…

Coupang真的好做吗?韩国Coupang入驻流程——站斧浏览器

coupang真的好做吗&#xff1f; Coupang自开放全球注册以来&#xff0c;一直备受跨境电商各平台卖家的关注&#xff0c;那么作为一颗跨境电商的新星&#xff0c;真的值得做吗&#xff1f; 不到一年的关注度遭到如此众多的跨境卖家追捧的平台&#xff0c;火是有他的原因的&…

【C++】C++STL详解(四)—— vector的模拟实现

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;C学习 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 【C】CSTL详解&#xff08;三&am…

pdf文件可以压缩大小吗?pdf压缩方法分享

在日常生活和工作中&#xff0c;我们经常需要处理大量的PDF文件。有时候&#xff0c;一个PDF文件的大小可能超过了几十MB&#xff0c;甚至无法通过电子邮件发送。那么&#xff0c;如何有效地压缩PDF文件大小呢&#xff1f;本文将为你介绍三个简单易行的方法&#xff0c;帮助你轻…

​2:DDD概念大白话

产品代码都给你看了&#xff0c;可别再说不会DDD&#xff08;二&#xff09;&#xff1a;DDD概念大白话 # 这是一个讲解DDD落地的文章系列&#xff0c;作者是《实现领域驱动设计》的译者滕云。本文章系列以一个真实的并已成功上线的软件项目——码如云&#xff08;https://www…

uni-app打包iOS ipa文件后不上架App store为用户提供下载解决过程记录

写在前面&#xff0c;itms-services协议是什么 itms-services协议是苹果提供的一种让iOS应用在用户设备上无线安装或升级的协议。 具体来说: itms-services表示iOS应用无线安装服务的URL方案,格式为:itms-services://?actiondownload-manifest&urlMANIFEST_URL其中MANIF…

28 WEB漏洞-XSS跨站之WAF绕过及安全修复

目录 常规WAF绕过思路标签语法替换特殊符号干扰提交方式更改垃圾数据溢出加密解密算法结合其他漏洞绕过 自动化工具说明强大的fuzzing引擎安全修复方案演示案例&#xff1a; 常规WAF绕过思路 标签语法替换 xss的效果可以由多个代码来实现&#xff0c;就类似于我们使用到的其它…

02Redis的命令行客户端和桌面客户端的下载和安装

Redis桌面客户端 安装完成Redis服务,我们就可以在Redis的客户端操作Redis的数据库实现数据的CRUD了,客户端分为三类命令行客户端, 图形化桌面客户端,编程客户端 命令行客户端 Redis安装完成后就自带了命令行客户端: redis-cli [options] [commonds] -h选项&#xff1a;指定…

爬虫抓取数据超时是什么原因?如何解决爬虫抓取数据超时问题?

网络爬虫是一种自动化程序&#xff0c;它可以在互联网上抓取数据并将其存储在本地数据库中。然而&#xff0c;有时候&#xff0c;网络爬虫会遇到超时错误&#xff0c;导致无法成功抓取数据。那么&#xff0c;网络爬虫抓取数据显示超时是什么原因呢&#xff1f; 网络连接问题 网…

KMeans算法全面解析与应用案例

目录 一、聚类与KMeans介绍聚类的基础概念KMeans算法的重要性 二、KMeans算法原理数据集和特征空间距离度量算法步骤 三、KMeans案例实战案例背景&#xff1a;客户细分数据集说明Python实现代码输出与解释 四、KMeans的优缺点优点计算效率高算法简单易于实现 缺点需要预设K值对…