首页 > 系统相关 >linux---简单模拟实现shell(内置命令的解析)

linux---简单模拟实现shell(内置命令的解析)

时间:2024-03-30 20:33:28浏览次数:30  
标签:parse shell index --- command linux 进程 line NULL

准备工作的知识

我们要模拟实现一个命令行解释器的话,需要运用进程替换的知识。我们用我,如花,王婆,实习生的例子来说:这里的“我”就是程序员,如花是操作系统,王婆是命令行解释器bash,实习生则是子进程,我们用户想要和操作系统交流的话,就需要通过bash,而命令行解释器(王婆)不会自己去执行我们的命令,而是会创建一个子进程(派实习生)去执行。这样的话,假如说子进程出了错误崩了,不会影响到父进程bash。

下面我们先贴代码:

 1 #include <stdio.h>
  2 #include <string.h>
  3 #include <unistd.h>
  4 #include <stdlib.h>
  5 #include <sys/types.h>
  6 #include <sys/wait.h>
  7 
  8 #define NUM 128
  9 #define SIZE 32
 10 
 11 char command_line[NUM];
 12 
 13 char* command_parse[SIZE];
 14 int main()
 15 {
 16   while(1)
 17   {
 18       //1.获取命令
 19       memset(command_line,'\0',sizeof(command_line));//初始化命令数组为全\0
 20       printf("[cyf@centos-myshell]$");
 21       fflush(stdout);
 22       if(fgets(command_line,NUM-1,stdin))         //在数组中获取命令
 23       {
 24         command_line[strlen(command_line)-1]='\0';//'\n' -> '\0'
 25       }
 26       //2.加工命令
 27       int index=0;
 28       command_parse[index]=strtok(command_line," ");//以空格分隔命令以及选项
 29       while(1)
 30       {
 31         index++;
 32         command_parse[index]=strtok(NULL," ");
 33         if(command_parse[index]==NULL)//当没有空格的时候,strtok函数会返回NULL,
 34                                       //并导致command_parse数组最后的内容为NULL
 35         {
 36           break;
 37         }
 38       }
 39       //3.执行命令
 40       if(fork()==0)                                                                                                                                                           
 41       {
 42         execvp(command_parse[0],command_parse);
 43         exit(1);//若父进程得到子进程退出码为1的话,说明子进程替换失败
 44       }
 45       int status=0;
 46       pid_t ret = waitpid(-1,&status,0);
 47       if(ret>0&&WIFEXITED(status))
 48       {
 49         printf("Exit Code:%d\n",WEXITSTATUS(status));
 50       }
 51   }
 52   return 0;
 53 }

结果如下:

模拟实现命令行解释器,上面的代码主要做三个任务。分别是获取命令(将命令用一个数组存储),加工命令(分割命令与选项),执行命令(通过进程替换,用子进程执行命令)。

我们会发现例如ls命令和原本的命令行解释器有些出入,我们写的命令行解释器没有显示颜色,这是因为shell执行ls指令的时候,会自动添加"-color=autp"的选项,我们可以用下面的代码,也添加上这个选项。

if(command_parse[0]!=NULL&&strcmp(command_parse[0],"ls")==0)
{
    index++;
    command_parse[index]="--color=auto";
}

现在就可以显示颜色了:

现在我们又出现了一个问题:当我们使用cd命令的时候,他并不像我们想的那样顺利执行,如图:

我们会发现,当前路径并不像我们所写的那样,指向上级目录。

对于这个问题,我们需要首先需要认识一下什么是当前工作目录:

我们通过ls /proc/24576 -al命令查看test进程的信息。

其中cwd代表当前程序的工作目录,exe代表当前执行的是磁盘中的哪个程序。

我们要明白:每个进程都有他自己的工作路径,我们在执行cd命令的时候,是先进行了fork(),创建了子进程,用子进程执行的cd命令,也就是说我们cd更改的是子进程的工作路径,跟父进程(myshell进程)没有关系,在子进程完成cd命令退出后,那么子进程的工作路径也就随之消失了。之后我们用pwd查看当前路径,也是先fork创建子进程,此时这个子进程刚刚被父进程创建出来,继承了父进程的性质,工作路径和父进程一致,而父进程的工作路径刚刚并没有发生任何变化,所以pwd打印出来的路径也没有任何变化。

而我们做cd命令的时候,我们内心期望改的是父进程的路径,我们要解决这个问题的话就需要不通过子进程修改工作目录,当我们发现第一个子串是cd的时候,就要直接更改工作目录,更改工作目录的工作就交给chdir函数来完成。

#include<unistd.h>

int chdir(const char* path);

这个函数只有路径一个参数,当我们使用cd命令的时候,cd后面跟着的就是我们要去的路径,所以我们就可以写出下面的代码:

if(command_parse[0]!=NULL&&strcmp(command_parse[0],"cd")==0)
{
    if(command_parse[1]!=NULL)
        {
            chdir(command_parse[1]);
            continue;
        }
}

结果为:

实际上,cd这样没有创建子进程去执行的命令叫做内置命令,就是shell内的一个函数调用,也就是说内置命令是shell自己做的,而不是创建子进程执行。

这个shell还有很多不完善的地方,后面我们会边学习边继续完善这个命令行解释器。

最后贴上完整代码:

  1 #include <stdio.h>
  2 #include <string.h>
  3 #include <unistd.h>
  4 #include <stdlib.h>
  5 #include <sys/types.h>
  6 #include <sys/wait.h>
  7 
  8 #define NUM 128
  9 #define SIZE 32
 10 
 11 char command_line[NUM];
 12 
 13 char* command_parse[SIZE];
 14 int main()
 15 {
 16   while(1)
 17   {
 18       //1.获取命令
 19       memset(command_line,'\0',sizeof(command_line));//初始化命令数组为全\0
 20       printf("[cyf@centos-myshell]$");
 21       fflush(stdout);
 22       if(fgets(command_line,NUM-1,stdin))         //在数组中获取命令
 23       {
 24         command_line[strlen(command_line)-1]='\0';//'\n' -> '\0'
 25       }
 26       //2.加工命令
 27       int index=0;
 28       command_parse[index]=strtok(command_line," ");//以空格分隔命令以及选项
 29       if(command_parse[0]!=NULL&&strcmp(command_parse[0],"ls")==0)
 30       {
 31           index++;
 32           command_parse[index]=(char*)"--color=auto";
 33       }
 34       while(1)
 35       {
 36         index++;
 37         command_parse[index]=strtok(NULL," ");
 38         if(command_parse[index]==NULL)//当没有空格的时候,strtok函数会返回NULL,
 39                                       //并导致command_parse数组最后的内容为NULL
 40         {                                                                                                                                                                     
 41           break;
 42         }
 43       }
 44       if(command_parse[0]!=NULL&&strcmp(command_parse[0],"cd")==0)
 45       {
 46           if(command_parse[1]!=NULL)
 47               {
 48                   chdir(command_parse[1]);
 49                   continue;
 50               }
 51       }
 52       //3.执行命令
 53       if(fork()==0)
 54       {
 55         execvp(command_parse[0],command_parse);
 56         exit(1);//若父进程得到子进程退出码为1的话,说明子进程替换失败
 57       }
 58       int status=0;
 59       pid_t ret = waitpid(-1,&status,0);
 60       if(ret>0&&WIFEXITED(status))
 61       {
 62         printf("Exit Code:%d\n",WEXITSTATUS(status));
 63       }
 64   }
 65   return 0;
 66 }

标签:parse,shell,index,---,command,linux,进程,line,NULL
From: https://blog.csdn.net/weixin_73919606/article/details/137151922

相关文章

  • Linux下history命令简单原理
    前言在我们平时操作linux服务器时,有时候需要使用之前操作过的命令,这个时候history就派上用场了,它会记录你的历史操作命令。使用历史记录会持久化存储,默认位置是当前用户目录下的.bash_history文件。当Linux系统启动一个Shell时,Shell会从.bash_history文件中,读取......
  • 毕业设计:基于深度学习的电影推荐算法 -- 以豆瓣为例 大数据
    目录前言设计思路一、课题背景与意义二、算法理论原理2.1GRU网络模型2.2语言模型2.3推荐算法三、检测的实现3.1数据集3.2实验环境搭建3.3实验及结果分析最后前言    ......
  • LInux: fork()究竟是如何工作的?为何一个变量能够接受两个返回值?
    LInux:fork函数究竟是如何工作的?为何一个变量能够接受两个返回值?前言一、fork()用法二、fork()应用实例展示三、fork()工作原理3.1为什么要创建子进程?3.2fork()究竟干了些什么?3.3fork为什么会存在两个返回值?3.5为何fork函数中父进程返回子进程的pid、子进程返回0?3.......
  • 鸿蒙HarmonyOS实战-ArkUI组件(Swiper)
    ......
  • Quiet-STaR:让语言模型在“说话”前思考
    大型语言模型(llm)已经变得越来越复杂,能够根据各种提示和问题生成人类质量的文本。但是他们的推理能力让仍然是个问题,与人类不同LLM经常在推理中涉及的隐含步骤中挣扎,这回导致输出可能在事实上不正确或缺乏逻辑。考虑以下场景:正在阅读一个复杂的数学证明。虽然最终的答案可能很......
  • LLVM-Tutorial
    FirstLanguageFrontendwithLLVMLexer一个简单的“递归下降”的单步词法分析器,结合“符号栈”和“向前看”一个字符完成单步词法单元的提取//===----------------------------------------------------------------------===////Lexer//===-----------------------......
  • HTB Perfection-wp 基于ruby的SSTI注入、密码爆破工具hashcat的使用、反弹shell的编码
    一进来发现这个页面,估计突破点就是在这里了 当然也可走一下固定的流程,nmap扫一下、dir爆破一下。这里不太像是sql的注入点(并不是查询功能),就不用sql试了。首先第一反应时看到WEBrick模板框架,考虑有没有版本漏洞,但是在几个CVE数据库搜一下都没有对应版本的漏洞。那么走一下正常......
  • Vulnhub之dc-6
    一信息收集IP扫描端口扫描访问80liux/etc/hosts访问成功页面信息插件目录扫描登录界面使用wpscan扫描一下用户用户字典选取带k01的字典进行扫爆破mark/helpdesk01登录二提权bp拦截kali监听写入交互式shell还有个22端口,可能有用发......
  • 《算法笔记》系列----质数的判断(埃氏筛法)
    目录一、朴素算法二、埃氏筛法1、与朴素算法对比2、算法介绍   3、例题即代码实现一、朴素算法 从素数的定义中可以知道,一个整数n要被判断为素数,需要判断n是否能被2.3.n-1中的一个整除。只2,3..n-1都不能整除n,n才能判定为素数,而只要有一个能整除n的数出现,n就......
  • c语言:用do-while输出前40项的斐波那契数值
    求Fibonacci数列的前40个元素。该数列的特点是第1、2两个数为1、1。从第3个数开始,每数是其前两个数之和。  分析:从题意可以用如下等式来表示斐波那契数列:     1,1,2,3,5,8,13,21…     f1=1     (n=1)     f2=1   ......