秒懂Linux之制作简易shell

news2024/12/26 17:44:02

fe594ea5bf754ddbb223a54d8fb1e7bc.gif

目录

一.全部代码

 二.自定义shell


一.全部代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>


#define SIZE 1024
#define MAX_ARGC 64
#define SEP " "
    
char *argv[MAX_ARGC];
char pwd[SIZE];
char env[SIZE]; // for test
int lastcode = 0;

const char* HostName()
{
    char *hostname = getenv("HOSTNAME");
    if(hostname) return hostname;
    else return "None";
}

const char* UserName()
{
    char *hostname = getenv("USER");
    if(hostname) return hostname;
    else return "None";
}

const char *CurrentWorkDir()
{
    char *hostname = getenv("PWD");
    if(hostname) return hostname;
    else return "None";
}

char *Home()
{
    return getenv("HOME");
}

int Interactive(char out[], int size)
{
    // 输出提示符并获取用户输入的命令字符串"ls -a -l"
    printf("[%s@%s %s]$ ", UserName(), HostName(), CurrentWorkDir());
    fgets(out, size, stdin);
    out[strlen(out)-1] = 0; //'\0', commandline是空串的情况?
    return strlen(out);
}

void Split(char in[])
{
    int i = 0;
    argv[i++] = strtok(in, SEP); // "ls -a -l"
    while(argv[i++] = strtok(NULL, SEP)); // 故意将== 写成 =
    if(strcmp(argv[0], "ls") ==0)
    {
        argv[i-1] = (char*)"--color";
        argv[i] = NULL;
    }
}

void Execute()
{
    pid_t id = fork();
    if(id == 0)
    {
        // 让子进程执行命名
        execvp(argv[0], argv);
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid == id) lastcode = WEXITSTATUS(status); 
    //printf("run done, rid: %d\n", rid);
}

int BuildinCmd()
{
    int ret = 0;
    // 1. 检测是否是内建命令, 是 1, 否 0
    if(strcmp("cd", argv[0]) == 0)
    {
        // 2. 执行
        ret = 1;
        char *target = argv[1]; //cd XXX or cd
        if(!target) target = Home();
        chdir(target);
        char temp[1024];
        getcwd(temp, 1024);
        snprintf(pwd, SIZE, "PWD=%s", temp);
        putenv(pwd);
    }
    else if(strcmp("export", argv[0]) == 0)
    {
        ret = 1;
        if(argv[1])
        {
            strcpy(env, argv[1]);
            putenv(env);
        }
    }
    else if(strcmp("echo", argv[0]) == 0)
    {
        ret = 1;
        if(argv[1] == NULL) {
            printf("\n");
        }
        else{
            if(argv[1][0] == '$')
            {
                if(argv[1][1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                }
                else{
                    char *e = getenv(argv[1]+1);
                    if(e) printf("%s\n", e);
                }
            }
            else{
                printf("%s\n", argv[1]);
            }
        }
    }
    return ret;
}

int main()
{
    while(1)
    {
        char commandline[SIZE];
        // 1. 打印命令行提示符,获取用户输入的命令字符串
        int n = Interactive(commandline, SIZE);
        if(n == 0) continue;
        // 2. 对命令行字符串进行切割
        Split(commandline);
        // 3. 处理内建命令
        n = BuildinCmd();
        if(n) continue;
        // 4. 执行这个命令
        Execute();
    }
   // for(int i=0; argv[i]; i++)
   // {
   //     printf("argv[%d]: %s\n", i, argv[i]);
   // }
    return 0;
}

 二.自定义shell

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
   
   #define SIZE 1024
   const char* HostName()
   {
       char *hostname = getenv("HOSTNAME");
       if(hostname) return hostname;
       else return "None";
   }
   
   const char* UserName()
   {
       char *hostname = getenv("USER");
       if(hostname) return hostname;
       else return "None";
   }
   
   const char *CurrentWorkDir()
   {
       char *hostname = getenv("PWD");
       if(hostname) return hostname;
       else return "None";
   }
   
   
   int main()
   {
   
       // 输出提示符并获取用户输入的命令字符串"ls -a -l"
      printf("[%s@%s %s]$ ", UserName(), HostName(), CurrentWorkDir());
      char commandline[SIZE];
      scanf("%s", commandline);
      printf("test: %s\n", commandline);                                                                                                                                                                                                                         
   
      return 0;
   
   }

首先我们先来模拟一下最开始的命令行及其输入~

已经有点味道了~ 就是功能有点单一,只能输入输出~

我们再执行一次模拟写入指令后发现当前缓冲区只能获取ls,后面遇到空格就刷新出来了。那要如何获取完整的指令呢?(不获取到完整的指令又怎么知道用户具体要干啥呢?)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
   
   #define SIZE 1024
   const char* HostName()
   {
       char *hostname = getenv("HOSTNAME");
       if(hostname) return hostname;
       else return "None";
   }
   
   const char* UserName()
   {
       char *hostname = getenv("USER");
       if(hostname) return hostname;
       else return "None";
   }
   
   const char *CurrentWorkDir()
   {
       char *hostname = getenv("PWD");
       if(hostname) return hostname;
       else return "None";
   }
   
   
   int main()
   {
   
       // 输出提示符并获取用户输入的命令字符串"ls -a -l"
      printf("[%s@%s %s]$ ", UserName(), HostName(), CurrentWorkDir());
      char commandline[SIZE];
      fgets(commandline,SIZE,stdin);
      
      commandline[strlen(commandline)-1] = 0;    
  
      printf("test: %s\n", commandline);                                                                                                                                                                                                                         
   
      return 0;
   
   }

这里我们借助函数fgets来获取行内容~因为fgets结尾会自带/n(换行),所以我们把结尾处的'/n'给换掉~

既然能够获取完整的输入命令,接下来我们就需要以空格为分隔符拆除若干个字符串存放在argv数组中~

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
   
   #define SIZE 1024
   #define MAX_ARGC 64
   #define SEP " "
   const char* HostName()
   {
       char *hostname = getenv("HOSTNAME");
       if(hostname) return hostname;
       else return "None";
   }
   
   const char* UserName()
   {
       char *hostname = getenv("USER");
       if(hostname) return hostname;
       else return "None";
   }
   
   const char *CurrentWorkDir()
   {
       char *hostname = getenv("PWD");
       if(hostname) return hostname;
       else return "None";
   } 

  int main()                                                                                                                                                                                                         {                                                                                                                                                                                                                                                                                                                                                                                                                                         
     // 输出提示符并获取用户输入的命令字符串"ls -a -l"                                                                                                                              
     printf("[%s@%s %s]$ ", UserName(), HostName(), CurrentWorkDir());                                                                                               
     char commandline[SIZE];                                                                                                                                                                                                                                                                                                                 
     fgets(commandline,SIZE,stdin);                                                                                                                                      
     commandline[strlen(commandline)-1] = 0;                                                                                                                        
                                                                                                                                                                             
                                                                                                                         
     //对命令行字符串进行切割                                                                                                                                             
     char* argv[MAX_ARGC];                                                                                                                                                   
     int i = 0;                                                                                                                                                      
     argv[i++] = strtok(commandline,SEP);
     while(argv[i++]=strtok(NULL,SEP));                                                                                                                                                                                                                                                                      
     for(int i = 0;argv[i];i++)                                                                                                                              
     {                                                                                                                                                      
     printf("argv[%d]: %s\n",i,argv[i]);                                                                                                                
     }                                                                                                                                                                                                                                                                                                                                                                   
     return 0;   
     
}

我们利用strtok进行字符串按空格(SEP)进行分割,若要继续分割则需要把参数换成NULL~

然后利用分割到最后剩下NULL的特性把所有输入命令都挨个存储到argv数组中~

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    
   
   #define SIZE 1024
   #define MAX_ARGC 64
   #define SEP " "
   const char* HostName()
   {
       char *hostname = getenv("HOSTNAME");
       if(hostname) return hostname;
       else return "None";
   }
   
   const char* UserName()
   {
       char *hostname = getenv("USER");
       if(hostname) return hostname;
       else return "None";
   }
   
   const char *CurrentWorkDir()
   {
       char *hostname = getenv("PWD");
       if(hostname) return hostname;
       else return "None";
   } 

  int main()                                                                                                                                                                                                         {                                                                                                                                                                                                                                                                                                                                                                                                                                         
     // 输出提示符并获取用户输入的命令字符串"ls -a -l"                                                                                                                              
     printf("[%s@%s %s]$ ", UserName(), HostName(), CurrentWorkDir());                                                                                               
     char commandline[SIZE];                                                                                                                                                                                                                                                                                                                 
     fgets(commandline,SIZE,stdin);                                                                                                                                      
     commandline[strlen(commandline)-1] = 0;                                                                                                                        
                                                                                                                                                                             
                                                                                                                         
     //对命令行字符串进行切割                                                                                                                                             
     char* argv[MAX_ARGC];                                                                                                                                                   
     int i = 0;                                                                                                                                                      
     argv[i++] = strtok(commandline,SEP);
     while(argv[i++]=strtok(NULL,SEP));  
    //执行命令                                                                                                                                                                                                                                                                    
    pid_t id = fork();
    if(id == 0)    
   {        // 让子进程执行命名
        execvp(argv[0], argv);
        exit(1);   
   }    
    int status = 0;
    pid_t rid = waitpid(id, NULL, 0);
    printf("run done,rid:%d\n", rid);                                                                                                                                                                                                                                                                                                                                                                 
    return 0;   
     
}

我们创建一个子进程,然后把存储好命名行指令的数组argv通过execvp函数进行进程替换~

就好比我们输入了ls -a -l,那么它们就会作为替换进程所需要执行的参数,通过这些参数找到真正的ls -a -l指令进而去调用~

最后再套上无限循环就可以一直使用我们的自定义shell了~

我们发现了一个问题:为什么cd指令无法生效呢? 明明其他命令都生效了~

因为我们这些命令最终都是子进程在执行的,所以当前执行cd命令的是子进程而不是当前的bash,

而子进程一执行就退出了,切换路径还有什么意义?所以是要让bash去切换才有用,然后我们才能看到路径的变化~

这种命令称为内建命令,不需要由子进程去执行,让其父进程去执行~

 

我们对内建指令cd进行特殊处理,如何获取到的指令中有cd那就标记为内建指令,并记录后面要去的路径,然后用chdir改变子进程的路径。若无则为NULL,cd直接到家目录下。

不过当前有一个问题,用pwd可以查到更新后的路径,但shell中打印的路径却没有更新~然后就是直接按回车会有空串的问题~

我们记录获取字符串的长度,如果空串0那就不要继续执行了~

至于路径变化我们只需要做到环境变量的更新就好了~

我们设置一个大小为SIZE的数组pwd,用来存储环境变量PWD~

通过snprintf函数把获取到的路径存储进pwd数组中,再通过putenv把这个环境变量进行更新或添加~

不过还是有点瑕疵,当执行cd ..的时候路径也会变成 .. 但这个明明是上一级路径~

我们可以借助函数getcwd来获取当前路径,因为路径我们已经切换过了,所以根据获取更新后的路径作为环境变量~

为什么我们在自定义shell中导入新的环境变量会看不到呢?因为子进程会退出,没必要~所以export也是内建命令

如果这样修改会发现env只有在第一次才可以查到新的环境变量,后面就看不到了。

因为argv是每一次都要去获取指令的,你虽然在这一次输入指令export被成功导入,但下一次的命令就没有了,这样就会覆盖argv,argv表又回归原状,找不到原来有export指令的表了,自然就找不到环境变量了

所以我们再定义一个关于环境变量的数组emv,然后导出的时候有env这个表在就不会因为argc覆盖问题而看不了环境变量了

我们再来获取一下进程的退出码~

void Execute()
{
    pid_t id = fork();
    if(id == 0)
    {
        // 让子进程执行命名
        execvp(argv[0], argv);
        exit(1);
    }
    int status = 0;
    pid_t rid = waitpid(id, &status, 0);
    if(rid == id) lastcode = WEXITSTATUS(status); 
    //printf("run done, rid: %d\n", rid);
}

然后对内建命令echo进行编写 

 else if(strcmp("echo", argv[0]) == 0)
    {
        ret = 1;
        if(argv[1] == NULL) {
            printf("\n");
        }
        else{
            if(argv[1][0] == '$')
            {
                if(argv[1][1] == '?')
                {
                    printf("%d\n", lastcode);
                    lastcode = 0;
                }
                else{
                    char *e = getenv(argv[1]+1);
                    if(e) printf("%s\n", e);
                }
            }
            else{
                printf("%s\n", argv[1]);
            }
        }
    }

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

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

相关文章

pnpm【实用教程】2024最新版

pnpm 简介 pnpm 全称 performant npm&#xff0c;即高性能的 npm&#xff0c;由 npm/yarn 衍生而来&#xff0c;解决了 npm/yarn 内部潜在的 bug&#xff0c;极大的优化了性能&#xff0c;扩展了使用场景&#xff0c;被誉为 最先进的包管理工具 安装 pnpm npm i -g pnpm使用 pn…

安全基础学习-SM4加密算法

SM4 是一种中国国家密码标准(GB/T 32907-2016)中定义的分组加密算法,又称为“中国商用密码算法SM4”。它是由中国国家密码管理局发布的,并广泛应用于金融、电子商务和其他需要数据加密的场景。 1、SM4 算法概述 SM4 是一种对称加密算法,意味着加密和解密使用相同的密钥。…

升级 kubeadm 部署的 k8s 集群

文章目录 一、环境二、注意事项1、备份数据2、跨版本升级3、支持的版本偏差3.1、kube-apiserver3.2、kubeklet3.3、kube-proxy3.4、kube-controller-manager、kube-scheduler 和 cloud-controller-manager3.5、kubectl 4、升级流程 三、版本升级&#xff08;补丁版本升级&#…

css 文字图片居中及网格布局

以下内容纯自已个人理解&#xff0c;直接上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><…

ervlet、jsp、Cookie、Ajax学习

系列文章目录 第一章 基础知识、数据类型学习 第二章 万年历项目 第三章 代码逻辑训练习题 第四章 方法、数组学习 第五章 图书管理系统项目 第六章 面向对象编程&#xff1a;封装、继承、多态学习 第七章 封装继承多态习题 第八章 常用类、包装类、异常处理机制学习 第九章 集…

Nginx 正则表达式与rewrite

目录 一、正则表达式 二、rewrite 2.1 rewrite简述 2.2 rewrite 跳转 2.3 rewrite 执行顺序 2.4 rewrite 语法格式 三、location 3.1 location 类别 3.2 location常用匹配规则 3.3 location优先级 3.4 示例说明 3.5 匹配规则总结 3.6 三个匹配规则定义 四、实战…

【业务场景实战】如何优雅地进行缓存预热?

从Java基础到中间件再到微服务&#xff0c;我们学了这么多&#xff0c;但遇到真实项目的时候&#xff0c;还是不会根据所学知识&#xff0c;对项目进行改造&#xff1b;或者太久不用早已忘记。学会用才是走得更远&#xff01; 缓存穿透、雪崩&#xff0c;大家都不陌生&#xf…

【Linux操作系统】——Centos7下安装python3.8

1.Centos7下安装python3.8 1.1 安装依赖包 yum install gcc openssl-devel bzip2-devel libffi-devel zlib-devel ncurses-devel sqlite-devel readline-devel readline-devel make在CentOS 7上安装Python时&#xff0c;‌需要安装一些必要的依赖包以确保Python的正常编译和运…

HarmonyOS NEXT - 项目基础框架的搭建

demo 地址: https://github.com/iotjin/JhHarmonyDemo 代码不定时更新&#xff0c;请前往github查看最新代码 HarmonyOS NEXT - 项目基础框架的搭建 序前置工作项目的目录结构主界面实现BaseTabBar代码实现子页面实现路由跳转登录页面和主页面切换登录实现退出登录 序 项目基于…

Spark-环境启动

一、概览 从start-all.sh开始捋&#xff0c;一直捋到Master、Worker的启动并建立通信 二、宏观描述 Master端 1、start-all.sh调用start-master.sh启动Master 2、执行org.apache.spark.deploy.master.Master中main方法 3、通过工厂模式创建RpcEnv子类NettyRpcEnv a、创建…

viper配置文件读取管理库 一个支持12种文件类型,5种远程协议的配置文件管理和加载工具库 使用方法示例

viper是一个不错的配置文件管理库&#xff0c; 他支持的配置文件类型依次有 "json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv&quo…

java:实现简单的验证码功能

效果 实现思路 验证码图片的url由后端的一个Controller生成&#xff0c;前端请求这个Controller接口的时候根据当前时间生成一个uuid&#xff0c;并把这个uuid在前端使用localStorage缓存起来&#xff0c;下一次还是从缓存中获取。 Controller生成验证码之后&#xff0c;把前…

Qt 学习第六天:页面布局

如何设计页面&#xff1f; 有个类似沙盒模式的玩法&#xff0c;Qt Widget Designer可以更好的帮助我们设计页面 点击.ui文件进入 右上方可以看到四种常见的布局&#xff1a; 四种布局 &#xff08;一&#xff09;水平布局horizontalLayout&#xff1a;QHBoxLayout H 是 hori…

c++实现B树(上)

哈喽啊&#xff01;好久不见&#xff0c;甚是想念&#xff01;失踪人口要回归了&#xff0c;时隔一个多月小吉我终于要更新blog了&#x1f389;。在停更的一个多月中&#xff0c;小吉也有在好好学习提升自己&#xff0c;立志给大家呈现好文章。  现在让我们进入正题吧&#xf…

初识C++:开启C++之旅

目录 1.C的第一个程序 2.namesapce命名空间域 2.1namespace的意义 2.2.2namespace的定义 2.3命名空间的使用 3.C输入/输出 4.缺省参数 5.函数重载 6.引用 6.1引用的特性 6.2引用的使用 1.C的第一个程序 c版本&#xff1a; #include<iostream>using std::cout…

kali安装wechart

前言&#xff1a; 突发奇想想在kali安装个wechart&#xff0c;试了下网上的很多&#xff0c;玩坏了一个虚拟机算是找到了一个不错的方法&#xff0c;这里记录下&#xff0c;防迷路 基础配置&#xff1a; 首先修改源&#xff1a; vim /etc/apt/sources.list 注释默认配置&…

EasyCVR视频汇聚平台:打造全栈视频监控系统的基石,解锁可视化管理与高效运维

随着科技的飞速发展&#xff0c;视频监控已成为现代社会不可或缺的一部分&#xff0c;广泛应用于社区、公共场所、工业领域等多个场景。EasyCVR视频汇聚平台&#xff0c;作为一款高性能的视频汇聚管理平台&#xff0c;凭借其强大的视频处理、汇聚与融合能力&#xff0c;在构建全…

centos8 安装zookeeper

1&#xff1a;下载 zookeeper官网 解压&#xff1a;tar -zxvf apache-zookeeper-3.6.3.tar.gz 修改自己想要的文件目录 mv apache-zookeeper-3.6.3 zookeeper_3.6.3 备份一下 配置文件 cp zoo_sample.cfg zoo.cfg vim zoo.cfg 编辑日志文件和端口号

nginx实战演练

目录 一.Nginx架构和安装&#xff08;未完待续&#xff09; <1>.Nginx概述 <2>.Nginx架构和进程 <3>.Nginx模块 <4>.Nginx安装(编译安装) 二.Nginx基础配置 <1>.关闭debug <2>.将nginx软件添加到环境变量 <3>.开机自启动脚…

EmguCV学习笔记 VB.Net 2.5 Mat类、Matrix类和Image类的相互转换

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV学习笔记目录 Vb.net EmguCV学习笔记目录 C# 笔者的博客网址&#xff1a;VB.Net-CSDN博客 教程相关说明以及如何获得pdf教…