demo xshell (程序替换 工作目录 内建命令)

1.程序替换

在学习完一些列的进程替换接口之后我们大概就能知道,我们的环境变量以及命令行参数是如何传递给子进程的,这些参数是我们在调用进程替换时就传给了子进程的数据。

那么如果我们自己要实现一个简单的命令行解释器,我们是不是首先就需要对命令行的参数进行解析? 命令行参数的解析我们首先需要获取一行字符串,然后以空格为间隔将字符串拆分为程序名和选项。

     #include<stdio.h>#include<assert.h>#include<string.h>#include<stdlib.h>#include<unistd.h>#include<sys/types.h>#include<sys/wait.h>char stringArg[1024]; //获取命令行输入char* argv[32]; //拆解后的命令行参数数组int main(){stringArg[1023]=0;//printf("%s\n",getenv("HOST"));while(1){memset(stringArg,0,1024); //全部初始化\0 ,便于计算长度memset(argv,0,32*sizeof(char*));//全部初始化为NULL,命令行数组的结尾必须是NULL//打印一行提示信息   用户名@主机名 当前路径$printf("[%s@ %s %s]$ ",getenv("USER"),getenv("HOSTNAME"),getenv("PWD")); //不换行fflush(stdout);   //将提示信息打印出来//获取字符串   要读空格,不能用 scanf ,使用fgetsfgets(stringArg,1023,stdin);stringArg[strlen(stringArg)-1]=0; //将最后的 \n 换成 \0 if(strlen(stringArg)==0){//什么也没输入continue;}//拆解字符串 strtokint i=0;argv[i++]=strtok(stringArg," ");while(argv[i++]=strtok(NULL," ")); //最后一次当字符串结束再使用strtok,会返回NULL,刚好作为循环结束条件#ifdef _DEBUG_ARGV//测试切割功能char**p=argv;while(*p)printf("%s ",*p++);printf("\n");#endif}return 0;}   

切割字符串的工作可以直接使用 strtok 函数来完成。

完成切割字符串的工作之后,我们就可以将该命令行数组用于程序替换的参数了,这是最基础的shell。

    //程序替换pid_t id=fork();if(id<0){perror("fork failed");exit(1);}if(id==0){//程序替换execvp(argv[0],argv);//如果替换失败则会执行下面的代码perror("execvp failed");exit(1);}//父进程等待回收僵尸int status=0;waitpid(id,&status,0);if(WIFEXITED(status))//正常退出{if(WEXITSTATUS(status))//退出码不为0 {printf("运行成功,退出码为 :%d\n",  WEXITSTATUS(status));}}else//异常终止{printf("%s\n",strerror(errno));}

2.进程工作目录

在上面的代码实现下还有很多的小问题,比如我们使用cd命令的时候并不能切换目录,这是为什么呢?

要理解这个问题,首先我们需要知道一个概念:进程的工作目录

当我们启动一个进程时,我们打开进程目录 /proc,找到我们的进程,然后进到我们进程的目录中,我们能够看到两个特殊的东西

exe就是我们的二进制可执行程序的位置,这个我们很好理解,而cwd是一串路径,这就是进程的工作目录,当我们启动一个进程时,一般是我们在哪个路径下启动的这个进程,工作路径就是当前所在路径。我们可以在其他路径下降这个程序跑起来观察一下。

当我们在上一级目录下将该程序跑起来,进程的工作目录就变成了执行运行命令时所在的路径,这就是进程的工作路径。

那么进程工作路径可以修改吗?

在命令行中,我们的cd命令就是修改进程工作路径的,也就是我们的shell的工作路径,所以我们经常能够通过cd命令进入不同的路径,我们的环境变量PWD其实严格上来说就是当前进程工作路径。 而在程序中,我们可以使用 chdir 来更改工作目录,

到了这里,我们就能知道为什么我们的shell的cd命令不起作用了 ,因为我们是通过创建子进程然后进行程序替换执行的cd命令,那么修改的就是子进程的工作目录,而子进程工作目录被修改时,发生写时拷贝,不会影响父进程。而我们pwd查的是父进程的工作目录,所以我们的cd命令其实不应该让子进程执行,而是由我们的父进程自己执行,因为cd命令要修改的是我们当前进程也就是父进程的工作目录。 

像这种不需要创建子进程来执行的,而是让shell自己执行的命令,叫做内建命令或者内置命令

比如我们前面提到的 echo 命令也是一个内建命令,这就是他为什么能够显示本地变量的原因,因为他不是通过创建子进程来执行的,而是shell自己执行。

如何模拟实现cd命令呢? 其实也很简单,在创建子进程之前判断一下  命令的第一个字串是否为 cd,如果是 cd ,我们就直接使用chdir来更换工作目录。同时我们还要判断一下是否切换成功。

            //内建命令if(strcmp("cd",argv[0])==0)  //cd{const char*changepath=argv[1];int ret=chdir(changepath);if(ret==-1){                                                                                                                                                                                               printf("%s\n",strerror(errno));}continue; }    

而如果是echo目录,我们也是直接在当前目录下执行,而是直接在父进程打印。但是echo还有一个问题,就是空格我们输入的内容,也需要打印,所以我们需要在切割第一个字串串之后判断是否为echo,如果为echo,后续就先不切割了,然后再判断是否有 $ 符号,也就是在stringArg[5]是否为$ ,如果为$ ,则需要去找我们的变量列表中的变量来打印。但是我们这里只支持环境变量就够了,其他的实现起来太复杂。

3.重定向

重定向的符号无非就是 >  >> <  ,而且不会出现在第一个子串上,所以只需要在将第一个基础命令切割出来之后,判断一下是否有重定向的符。判断完之后将重定向符号变为空格,以便切割。

同时,重定向只能在子进程中去替换 fd ,也就是在程序替换之前替换,防止父进程也被替换了

            //判断是否有重定向int j=0;for(j=0;j<strlen(stringArg);++j){if(stringArg[j]=='<'){//输入重定向MODE=CHANGEIN;stringArg[j]='\0';filename=&stringArg[j+1];break;}else if(stringArg[j]=='>'){stringArg[j]='\0';if(stringArg[j+1]=='>'){j++;MODE=CHANGEAPPEND;stringArg[j]='\0';filename=&stringArg[j+1];break;}else{MODE=CHANGEOUT;filename=&stringArg[j+1];break;}}}
                  if(id==0){//先检查是否有重定向if(MODE==CHANGEOUT){int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);dup2(fd,1);}if(MODE==CHANGEIN){int fd=open(filename,O_RDONLY);dup2(fd,0);}if(MODE==CHANGEAPPEND){int fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);dup2(fd,1);}//程序替换execvp(argv[0],argv);//如果替换失败则会执行下面的代码perror("execvp failed");exit(1);}

为什么要把重定向符号设置为 \0 呢?因为我们不想要在切割字串的时候还将后面的文件名也切割进去,我们默认这些符号后面就是目标的文件名了。

完整代码

      #include<stdio.h>2 #include<assert.h>3 #include<string.h>4 #include<stdlib.h>5 #include<unistd.h>6 #include<sys/types.h>7 #include<sys/wait.h>8 #include<errno.h>9 #include<sys/stat.h>10 #include<fcntl.h>11 12 13 char stringArg[1024]; //获取命令行输入14 char* argv[32]; //拆解后的命令行参数数组15 #define CHANGEOUT 1  //输出重定向16 #define CHANGEIN 2  //输入重定向17 #define CHANGEAPPEND 4  //追加重定向18 char*filename; //重定向的目标文件19 int MODE;  //记录是否重定向20 21 22 int main()23 {24     stringArg[1023]=0;25     //printf("%s\n",getenv("HOST"));26 27     while(1)28     {  29         //重置errno30         errno=0;31         MODE =0 ;32 33         //获取字符串提取命令行34 35         memset(stringArg,0,1024); //全部初始化\0 ,便于计算长度36         memset(argv,0,32*sizeof(char*));37         //打印一行提示信息   用户名@主机名 当前路径$38         printf("[%s@ %s %s]$ ",getenv("USER"),getenv("HOSTNAME"),getenv("PWD"));   //不换行39         fflush(stdout);   //将提示信息打印出来40         //获取字符串   要读空格,不能用 scanf ,使用fgets41         fgets(stringArg,1023,stdin);42         stringArg[strlen(stringArg)-1]=0; //将最后的 \n 换成 \0 43         if(strlen(stringArg)==0)44         {45             //什么也没输入46             continue;47         }48         49         //判断是否有重定向50         int j=0; for(j=0;j<strlen(stringArg);++j)                                                                                                                                                                    52         {53             if(stringArg[j]=='<')54             {55                 //输入重定向56                 MODE=CHANGEIN;57                 stringArg[j]='\0';58                 filename=&stringArg[j+1];59                 break;60             }61             else if(stringArg[j]=='>')62             {63                 stringArg[j]='\0';64                 if(stringArg[j+1]=='>')65                 {66                     j++;67                     MODE=CHANGEAPPEND;68                     stringArg[j]='\0';69                     filename=&stringArg[j+1];70                     break;71                 }72                 else73                 {74                     MODE=CHANGEOUT;75                     filename=&stringArg[j+1];76                     break;77                 }78             }79         }80 81         //拆解字符串 strtok82         int i=0;83         argv[i++]=strtok(stringArg," ");84         if(strcmp("echo",argv[0])==0&&MODE==0) //echo85         {86             //检查$87             if(stringArg[5]=='$')88             {89                 //只考虑环境变量90                 printf("%s\n",getenv(&stringArg[6]));91             }92             else93                 printf("%s\n",&stringArg[5]);94             continue;95         }
W> 96         while(argv[i++]=strtok(NULL," ")); //最后一次当字符串结束再使用strtok,会返回NULL,刚好作为结束条件以及命令行参数数组的结尾NULL97        98 99         //内建命令100         if(strcmp("cd",argv[0])==0)  //cd{102             const char*changepath=argv[1];                                                                                                                                                                  103             int ret=chdir(changepath);104             if(ret==-1)105             {106                 printf("%s\n",strerror(errno));107             }108             continue; 109             110         }111 112         //程序替换113         else114         {115             pid_t id=fork();116          117              if(id<0)118              {119                  perror("fork failed");120                  exit(1);121              }122              if(id==0)123              {124             //先检查是否有重定向125             if(MODE==CHANGEOUT)126             {127                 int fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC,0666);128                 dup2(fd,1);129             }130             if(MODE==CHANGEIN)131             {132                 int fd=open(filename,O_RDONLY);133                 dup2(fd,0);134             }135             if(MODE==CHANGEAPPEND)136             {137                 int fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0666);138                 dup2(fd,1);139             }140                  //程序替换141                  execvp(argv[0],argv);142                  //如果替换失败则会执行下面的代码143                  perror("execvp failed");144                  exit(1);145              }146 147              //父进程等待回收僵尸148              int status=0;149              waitpid(id,&status,0);150              if(WIFEXITED(status))//正常退出{152                  if(WEXITSTATUS(status))//退出码不为0 153                  {154                      printf("运行成功,退出码为 :%d\n",  WEXITSTATUS(status));155                  }156              }157              else//异常终止158              {159                  printf("%s\n",strerror(errno));160              }161         }162 163 164 #ifdef _DEBUG_ARGV//测试切割功能165         char**p=argv;166         while(*p)167             printf("%s ",*p++);168         printf("\n");169 #endif170     }171 172 173     return 0;174 }

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

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

相关文章

stm32MP135裸机编程:使用USB/UART烧录程序到SD卡并从SD卡启动点亮一颗LED灯

0 参考资料 轻松使用STM32MP13x - 如MCU般在cortex A核上裸跑应用程序.pdf STM32CubeProgrammer v2.16.0 烧录需要的二进制文件1 烧录到SD卡需要哪些文件 参考《轻松使用STM32MP13x - 如MCU般在cortex A核上裸跑应用程序》&#xff0c;烧录需要的SD卡文件如下&#xff1a; &a…

教育的数字化转型——Kompas.ai如何变革学习体验

引言 在现代教育中&#xff0c;数字化转型逐渐成为提升学习效果的重要手段。随着科技的进步&#xff0c;人工智能&#xff08;AI&#xff09;在教育领域的应用越来越广泛。本文将探讨教育数字化转型的发展趋势&#xff0c;并介绍Kompas.ai如何通过AI技术变革学习体验。 教育数…

【Python】易错点——数组;列表;内存分配

在很多其他语言中用到的数组&#xff0c;没有展现出比Python的列表结构有更强的性能。 在许多其他编程语言中&#xff0c;数组通常是一种基本的数据结构&#xff0c;它们在内存中以连续的方式存储数据&#xff0c;这使得数组在某些操作上具有较高的性能。例如&#xff0c;数…

LNMP配置

文章目录 一、相关概念CGI的由来FastCGIPHP-FPM 二、编译安装编译安装nginxyum安装mysql编译安装php配置nginx支持php解析增加数据库安装论坛 一、相关概念 CGI的由来 最早的Web服务器只能简单地响应浏览器发来的HTTP请求&#xff0c;并将存储在服务器上的HTML文件返回给浏览器…

gdb 【Linux】

程序发布方式&#xff1a;  1、debug版本&#xff1a;程序会被加入调试信息&#xff0c;以便于进行调试。  2、release版本&#xff1a;不添加任何调试信息&#xff0c;是不可调试   确定一个可执行程序是debug&#xff0c;还是release [cxqiZ7xviiy0goapxtblgih6oZ test_g…

LIMS系统在汽车第三方检测实验室的应用

随着汽车行业的快速发展&#xff0c;汽车第三方检测实验室的工作量不断增加&#xff0c;对实验室的管理效率和数据准确性提出了更高的要求。LIMS系统的引入可以实现实验室的全面数字化管理&#xff0c;提高工作效率&#xff0c;降低运营成本&#xff0c;并提升数据质量与决策支…

python科研做图系列之时序图的绘制——对比折线图

参考知乎 折线图 我需要从两个不同的excel都读取第一列作为时间列,第二列作为编码列。 在同一张图上画出两条时间序列的折线图 横坐标是分钟,纵坐标是编码 帮我画的好看一些,记得解决中文乱码问题 英文版折线图 ,先搞个英文版,导师要求中文的话,再换成中文版 impor…

[Algorithm][动态规划][完全背包问题][零钱兑换][零钱兑换Ⅱ][完全平方数]详细讲解

目录 1.零钱兑换1.题目链接2.算法原理详解3.代码实现 2.零钱兑换 II1.题目链接2.算法原理详解3.代码实现 3.完全平方数1.题目链接2.算法原理详解3.代码实现 1.零钱兑换 1.题目链接 零钱兑换 2.算法原理详解 思路&#xff1a; 确定状态表示 -> dp[i][j]的含义 dp[i][j]&am…

QSqlDatabase、QSqlQuery、QSqlRecord、Sqlite用法

使用QSqlDatabase、QSqlQuery、QSqlRecord、Sqlite数据库实现一个简单的界面查询 1. 创建Sqlite数据库&#xff0c;表 mainwindow.cpp #include "mainwindow.h" #include "ui_mainwindow.h" #include "QSqlDatabase" #include "QSqlQuery&q…

【计算机毕业设计】273基于微信小程序的刷题系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

算法训练营day52

题目1&#xff1a;123. 买卖股票的最佳时机 III - 力扣&#xff08;LeetCode&#xff09; class Solution { public:int maxProfit(vector<int>& prices) {vector<vector<int>> dp(prices.size(), vector<int>(4,0));dp[0][0] -prices[0];dp[0][…

JavaSE面试

①.简述面向对象的三大特征 封装、继承、多态 1.封装&#xff1a; 概念&#xff1a; 是将类的某些信息隐藏在类的内部&#xff0c;不允许外部程序直接访问&#xff0c;而是通过该类提供的方法来实现对隐藏信息的操作和访问。 好处 &#xff1a; ①便于修改&#xff0c;增强了代…

前端实现流文件下载

在现代Web开发中&#xff0c;经常会遇到需要从服务器下载文件的情况。有时候这些文件是事先存储好的&#xff0c;可以通过简单的URL链接直接下载&#xff1b;但有时候&#xff0c;我们需要从数据流中动态生成文件并将其提供给用户。本篇博客将介绍如何在前端实现流文件下载的完…

MySQL8基于GTID以及VIP实现高可用主从架构

jdbc客户端配置高可用以及故障切换 jdbc客户端实现故障切换 MySQL Connector/J 支持服务器故障转移。当底层活动连接发生与连接相关的错误时&#xff0c;就会发生故障转移 参考官网地址 jdbc:mysql://[primary host][:port],[secondary host 1][:port] jdbc客户端实现故障切…

C# String

String字符串字面量字符串连接字符串是不可变的字符串比较字符串方法字符串插值字符串和字符数组字符串格式化空字符串和 null字符串的安全性正则表达式 注意String.Format基本语法参数 基本使用使用索引指定对齐和宽度使用格式字符串组合使用 总结 C# String 在C#中&#xff0…

vAttention:用于在没有Paged Attention的情况下Serving LLM

文章目录 0x0. 前言&#xff08;太长不看版&#xff09;0x1. 摘要0x2. 介绍&背景0x3. 使用PagedAttention模型的问题0x3.1 需要重写注意力kernel0x3.2 在服务框架中增加冗余0x3.3 性能开销0x3.3.1 GPU上的运行时开销0x3.3.2 CPU上的运行时开销 0x4. 对LLM服务系统的洞察0x5…

自动驾驶跟驰仿真

联合仿真需求分析报告 一、项目背景 随着汽车技术的快速发展&#xff0c;自动驾驶和智能网联汽车已成为行业发展的重要趋势。为确保自动驾驶车辆在复杂交通环境中的安全性和可靠性&#xff0c;进行联合仿真测试显得尤为重要。本报告旨在明确联合仿真的具体需求&#xff0c;为…

Springboot结合redis实现关注推送

关注推送 Feed流的模式 Timeline:不做内容筛选&#xff0c;简单的按照内容发布时间排序。常用于好友与关注。例如朋友圈的时间发布排序。 优点:信息全面&#xff0c;不会有缺失。并且实现也相对简单 缺点:信息噪音较多&#xff0c;用户不一定感兴趣&#xff0c;内容获取效率…

佳能5DMARK IV mov视频覆盖的恢复方法

5DMARK IV算是佳能比较经典的一款摄像机&#xff0c;是佳能早期使用MOV的摄像机之一&#xff0c;MOV是当初佳能高端机的象征&#xff0c;当然现在佳能已经不在通过MOV和MP4来区分硬件级别了。下边这个案例是文件拍摄时断电&#xff0c;结果变成0字节&#xff0c;然后覆盖了部分…

mysql实现json数据的解析

在MySQL中&#xff0c;你可以使用JSON函数来解析、查询和修改JSON数据。MySQL 5.7及更高版本提供了对JSON的原生支持。 以下是一些常用的JSON函数及其用法&#xff1a; JSON_EXTRACT(): 从JSON文档中提取数据。 sql SELECT JSON_EXTRACT({"name": "John"…