Shell自定义(二)

news2024/12/22 13:28:17

1.Shell自定义

1.初始化

定义全局变量environ,把g_env的内容用memset初始化为0,这里用malloc开辟的空间为对应环境变量的长度+1,多1位置是最后结束符0,strcpy把此时的对应的环境变量拷贝到g_env里面,下面是新增一个环境变量,要把最后一位置成NULL,最后循环把环境变量再导入进去,在循环体内,putenv 函数被用来将 g_env 数组中的每个字符串设置为环境变量。putenv 函数接受一个格式为 KEY=VALUE 的字符串,并将其添加到进程的环境变量中。如果 KEY 已经存在,则其值会被更新为 VALUE,最后把environ指向g_env。

void InitEnv()
{
    extern char **environ;
    memset(g_env, 0, sizeof(g_env));
    g_envs = 0;

    //本来要从配置文件来
    //1. 获取环境变量
    for(int i = 0; environ[i]; i++)
    {
        // 1.1 申请空间
        g_env[i] = (char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i], environ[i]);
        g_envs++;
    }
    g_env[g_envs++] = (char*)"HAHA=for_test"; //for_test
    g_env[g_envs] = NULL;

    //2. 导成环境变量
    for(int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}

2.检测内建命令

读取g_argv的第一个字符判断是否为内建命令。

bool CheckAndExecBuiltin()
{
    std::string cmd = g_argv[0];
    if(cmd == "cd")
    {
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
        Echo();
        return true;
    }
    else if(cmd == "export")
    {
    }
    else if(cmd == "alias")
    {
       // std::string nickname = g_argv[1];
       // alias_list.insert(k, v);
    }

    return false;
}

3.内建命令实现

1.Cd

 内建命令要在父进程实现,用chdir函数改变当前路径,要判断是否有命令行参数,用g_argv的第二个字符得知要到哪一个路径。

2.Echo

这里需要命令行参数达到2,然后用opt指向第二个字符,如果opt(这里为指针)的内容与$?就要看退出码的值,lastcode的值在子进程结束用WEXITSTATUS得到了,如果opt的第二个字符(char)等于'$'(""是一个字符串const char*类型),表示要打印环境变量的值opt.substr(1)构造从opt索引1到尾的子串(但是这里argc为2所以这个子串只有一个字符就是环境变量名字),env_value获取getenv得到的字符串值。

bool Cd()
{
    // cd argc = 1
    if(g_argc == 1)
    {
        std::string home = GetHome();
        if(home.empty()) return true;
        chdir(home.c_str());
    }
    else
    {
        std::string where = g_argv[1];
        // cd - / cd ~
        if(where == "-")
        {
            // Todu
        }
        else if(where == "~")
        {
            // Todu
        }
        else
        {
            chdir(where.c_str());
        }
    }
    return true;
}

void Echo()
{
    if(g_argc == 2)
    {
        // echo "hello world"
        // echo $?
        // echo $PATH
        std::string opt = g_argv[1];
        if(opt == "$?")
        {
            std::cout << lastcode << std::endl;
            lastcode = 0;
        }
        else if(opt[0] == '$')
        {
            std::string env_name = opt.substr(1);
            const char *env_value = getenv(env_name.c_str());
            if(env_value)
                std::cout << env_value << std::endl;
        }
        else
        {
            std::cout << opt << std::endl;
        }
    }
}

3.更新pwd

在cd后查看环境变量的pwd,不写更新的话是不变的,getcwd获取当前路径,如果不为空就写入到cwdenv里面,然后把"PWD=%s"和cwd(路径名)添加到环境变量里面,因为以及存在所以会更新值,完成更新路径。

const char *GetPwd()
{
    //const char *pwd = getenv("PWD");
    const char *pwd = getcwd(cwd, sizeof(cwd));
    if(pwd != NULL)
    {
        snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
        putenv(cwdenv);
    }
    return pwd == NULL ? "None" : pwd;
}

补充:

`chdir` 函数是用于改变当前工作目录的函数,其原型定义在 `<unistd.h>` 头文件中。以下是 `chdir` 函数的详细参数解释:

### 函数原型
```c
int chdir(const char *path);
```

### 参数
- `path`:这是一个指向字符的指针,表示要切换到的目标目录的路径。这个路径可以是绝对路径,也可以是相对路径。指定的目录必须存在,否则函数调用会失败。

### 返回值
- 成功:返回 `0`。
- 失败:返回 `-1`,并设置 `errno` 以指示错误原因。

### 错误处理
当 `chdir` 函数失败时,可以通过检查 `errno` 来确定错误原因。常见的错误包括但不限于:
- `ENOENT`:路径不存在。
- `ENOTDIR`:路径中的某个组件不是目录。
- `EACCES`:没有权限访问目标目录。

### 示例代码
```c
#include <unistd.h>
#include <stdio.h>

int main() {
    const char *path = "/path/to/directory";
    if (chdir(path) == 0) {
        printf("成功切换到目录: %s\n", path);
    } else {
        perror("切换目录失败");
    }
    return 0;
}
```

在这个示例中,如果路径切换成功,将打印出成功信息;如果失败,将输出错误信息。
 

在 C++ 中,`substr` 是 `std::string` 类的一个成员函数,用于返回字符串的一个子串。这个函数可以有两种形式:

1. 接受一个参数的版本:
   - `substr(size_t pos = 0)`:返回从位置 `pos` 开始到字符串末尾的子串。如果 `pos` 大于字符串的长度,将返回一个空字符串。

2. 接受两个参数的版本:
   - `substr(size_t pos, size_t len)`:返回从位置 `pos` 开始、长度为 `len` 的子串。如果 `pos` 大于字符串的长度,同样返回一个空字符串。如果 `pos + len` 超出了字符串的长度,那么子串将只包含从 `pos` 到字符串末尾的部分。

在你提到的 `substr(1)` 中,`1` 是传递给 `substr` 函数的第一个版本的第一个参数,表示从字符串的第二个字符开始提取子串,直到字符串的末尾。在 C++ 中,字符串的索引是从 `0` 开始的,所以 `1` 表示第二个字符。

下面是一个使用 `substr(1)` 的简单示例:

```cpp
#include <iostream>
#include <string>

int main() {
    std::string str = "Hello, World!";
    std::string sub = str.substr(1); // 从索引1开始,提取到字符串末尾的子串
    std::cout << sub << std::endl; // 输出 "ello, World!"
    return 0;
}
```

在这个例子中,`substr(1)` 将返回 "ello, World!",因为它从字符串 "Hello, World!" 的第二个字符开始提取,直到字符串的末尾。
 

`getcwd` 函数的参数如下:

1. `buf`:这是一个指向字符数组的指针,用于存储当前工作目录的路径。

2. `size`:这是一个`size_t`类型的值,表示`buf`数组的大小,即可以存储的最大字符数。

使用`getcwd`函数时,第一个参数`buf`是用于接收当前工作目录路径的缓冲区,第二个参数`size`是这个缓冲区的大小。如果`buf`为`NULL`且`size`为0,`getcwd`会动态分配内存(需要手动释放)来存储路径。
 

总代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstring>
#include <unordered_map>

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

// 下面是shell定义的全局数据

// 1. 命令行参数表
#define MAXARGC 128
char *g_argv[MAXARGC];
int g_argc = 0; 

// 2. 环境变量表
#define MAX_ENVS 100
char *g_env[MAX_ENVS];
int g_envs = 0;

// 3. 别名映射表
std::unordered_map<std::string, std::string> alias_list;

// for test
char cwd[1024];
char cwdenv[1024];

// last exit code
int lastcode = 0;

const char *GetUserName()
{
    const char *name = getenv("USER");
    return name == NULL ? "None" : name;
}

const char *GetHostName()
{
    const char *hostname = getenv("HOSTNAME");
    return hostname == NULL ? "None" : hostname;
}

const char *GetPwd()
{
    //const char *pwd = getenv("PWD");
    const char *pwd = getcwd(cwd, sizeof(cwd));
    if(pwd != NULL)
    {
        snprintf(cwdenv, sizeof(cwdenv), "PWD=%s", cwd);
        putenv(cwdenv);
    }
    return pwd == NULL ? "None" : pwd;
}

const char *GetHome()
{
    const char *home = getenv("HOME");
    return home == NULL ? "" : home;
}

void InitEnv()
{
    extern char **environ;
    memset(g_env, 0, sizeof(g_env));
    g_envs = 0;

    //本来要从配置文件来
    //1. 获取环境变量
    for(int i = 0; environ[i]; i++)
    {
        // 1.1 申请空间
        g_env[i] = (char*)malloc(strlen(environ[i])+1);
        strcpy(g_env[i], environ[i]);
        g_envs++;
    }
    g_env[g_envs++] = (char*)"HAHA=for_test"; //for_test
    g_env[g_envs] = NULL;

    //2. 导成环境变量
    for(int i = 0; g_env[i]; i++)
    {
        putenv(g_env[i]);
    }
    environ = g_env;
}

//command
bool Cd()
{
    // cd argc = 1
    if(g_argc == 1)
    {
        std::string home = GetHome();
        if(home.empty()) return true;
        chdir(home.c_str());
    }
    else
    {
        std::string where = g_argv[1];
        // cd - / cd ~
        if(where == "-")
        {
            // Todu
        }
        else if(where == "~")
        {
            // Todu
        }
        else
        {
            chdir(where.c_str());
        }
    }
    return true;
}

void Echo()
{
    if(g_argc == 2)
    {
        // echo "hello world"
        // echo $?
        // echo $PATH
        std::string opt = g_argv[1];
        if(opt == "$?")
        {
            std::cout << lastcode << std::endl;
            lastcode = 0;
        }
        else if(opt[0] == '$')
        {
            std::string env_name = opt.substr(1);
            const char *env_value = getenv(env_name.c_str());
            if(env_value)
                std::cout << env_value << std::endl;
        }
        else
        {
            std::cout << opt << std::endl;
        }
    }
}

// / /a/b/c
std::string DirName(const char *pwd)
{
#define SLASH "/"
    std::string dir = pwd;
    if(dir == SLASH) return SLASH;
    auto pos = dir.rfind(SLASH);
    if(pos == std::string::npos) return "BUG?";
    return dir.substr(pos+1);
}

void MakeCommandLine(char cmd_prompt[], int size)
{
    snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), DirName(GetPwd()).c_str());
    //snprintf(cmd_prompt, size, FORMAT, GetUserName(), GetHostName(), GetPwd());
}

void PrintCommandPrompt()
{
    char prompt[COMMAND_SIZE];
    MakeCommandLine(prompt, sizeof(prompt));
    printf("%s", prompt);
    fflush(stdout);
}

bool GetCommandLine(char *out, int size)
{
    // ls -a -l => "ls -a -l\n" 字符串
    char *c = fgets(out, size, stdin);
    if(c == NULL) return false;
    out[strlen(out)-1] = 0; // 清理\n
    if(strlen(out) == 0) return false;
    return true;
}

// 3. 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
bool CommandParse(char *commandline)
{
#define SEP " "
    g_argc = 0;
    // 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
    g_argv[g_argc++] = strtok(commandline, SEP);
    while((bool)(g_argv[g_argc++] = strtok(nullptr, SEP)));
    g_argc--;
    return g_argc > 0 ? true:false;
}

void PrintArgv()
{
    for(int i = 0; g_argv[i]; i++)
    {
        printf("argv[%d]->%s\n", i, g_argv[i]);
    }
    printf("argc: %d\n", g_argc);
}

bool CheckAndExecBuiltin()
{
    std::string cmd = g_argv[0];
    if(cmd == "cd")
    {
        Cd();
        return true;
    }
    else if(cmd == "echo")
    {
        Echo();
        return true;
    }
    else if(cmd == "export")
    {
    }
    else if(cmd == "alias")
    {
       // std::string nickname = g_argv[1];
       // alias_list.insert(k, v);
    }

    return false;
}

int Execute()
{
    pid_t id = fork();
    if(id == 0)
    {
        //child
        execvp(g_argv[0], g_argv);
        exit(1);
    }
    int status = 0;
    // father
    pid_t rid = waitpid(id, &status, 0);
    if(rid > 0)
    {
        lastcode = WEXITSTATUS(status);
    }
    return 0;
}

int main()
{
    // shell 启动的时候,从系统中获取环境变量
    // 我们的环境变量信息应该从父shell统一来
    InitEnv();

    while(true)
    {
        // 1. 输出命令行提示符
        PrintCommandPrompt();

        // 2. 获取用户输入的命令
        char commandline[COMMAND_SIZE];
        if(!GetCommandLine(commandline, sizeof(commandline)))
            continue;

        // 3. 命令行分析 "ls -a -l" -> "ls" "-a" "-l"
        if(!CommandParse(commandline))
            continue;
        //PrintArgv();

        // 检测别名
        // 4. 检测并处理内键命令
        if(CheckAndExecBuiltin())
            continue;

        // 5. 执行命令
        Execute();
    }
    //cleanup();
    return 0;
}



















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

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

相关文章

概率论得学习和整理27:关于离散的数组 随机变量数组的均值,方差的求法3种公式,思考和细节。

目录 1 例子1&#xff1a;最典型的&#xff0c;最简单的数组的均值&#xff0c;方差的求法 2 例子1的问题&#xff1a;例子1只是1个特例&#xff0c;而不是普遍情况。 2.1 例子1各种默认假设&#xff0c;导致了求均值和方差的特殊性&#xff0c;特别简单。 2.2 我觉得 加权…

模型 QFD(质量功能展开/质量屋)

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。将客户需求转化为产品设计。 1 模型 QFD&#xff08;质量功能展开&#xff09;的应用 1.1 电信服务及网络维护过程质量改进QFD应用案例 背景介绍&#xff1a; 随着中国加入WTO和国家对电信管制的普遍…

ECharts柱状图-柱图38,附视频讲解与代码下载

引言&#xff1a; 在数据可视化的世界里&#xff0c;ECharts凭借其丰富的图表类型和强大的配置能力&#xff0c;成为了众多开发者的首选。今天&#xff0c;我将带大家一起实现一个柱状图图表&#xff0c;通过该图表我们可以直观地展示和分析数据。此外&#xff0c;我还将提供…

Android XR 应用程序开发 | 从 Unity 6 开发准备到应用程序构建的步骤

在本文中 将解释为 Android XR 开发做准备的步骤&#xff0c;从下载 Unity 6 到项目和构建。 &#xff08;这次我将解释使用 Mac 的设置&#xff0c;但 Windows 的过程通常相同。 &#xff09; 如果您的许可证是 Personal&#xff0c;您仍然可以使用 Unity 开发 Android XR 应…

204页PPT金税四期监管要求与最新政策及风险防范-培训课件

这份资料是关于金税四期监管要求、最新政策及风险防范的培训课件&#xff0c;包含204页PPT内容。核心内容涉及金税四期的发展历程、与金税三期的主要变化、指挥决策系统、税务风险预警指标、重点监控行为、税务稽查重点领域、避税方法及处罚、以及企业自查和税务风险管控原理。…

比较器基本玩法

1 红外水龙头 1 比较器的工作特性 2 红外接收二极管的工作原理 1 红外发射二极管和红外接收二极管 2 红外接收二极管接收到&#xff0c;产生的电流是100uA&#xff0c;即电压等于4V 3 自动出水水龙头 4 比较器内部结构 1 有一些比较器需要加上拉电阻&#xff08;类似开漏输…

基于Qt的登陆界面设计

目标 自由发挥登录界面的应用场景&#xff0c;实现一个登录窗口的界面。 要求&#xff1a;每行代码都要有注释 代码 // 设置窗口大小为600x400像素 this->resize(600,400); // 设置窗口标题为"TheWitcher 巫师3&#xff1a;狂猎" this->setWindowTitle(&qu…

数据库建模工具 PDManer

数据库建模工具 PDManer 1.PDManer简介2.PDManer使用 1.PDManer简介 PDManer&#xff08;元数建模&#xff09;是一款功能强大且易于使用的开源数据库建模工具。它不仅支持多种常见数据库&#xff0c;如MySQL、PostgreSQL、Oracle、SQL Server等&#xff0c;还特别支持国产数据…

排序算法深度好文(图解 + 代码解析 + 误区 QA )——学排序看这一篇就够了!!!

排序算法 一、概述 1. 内部排序 (1) 冒泡排序 (2) 选择排序 a. 简单选择排序 b. 堆排序 (3) 插入排序 a. 直接插入排序 b. 希尔排序 (4) 快速排序 快速排序 hoare 版本 快速排序挖坑法 优化一随机化快速排序 优化二中位数三分法 (5) 归并排序 (6) 基数排序 2. …

【C语言程序设计——基础】顺序结构程序设计(头歌实践教学平台习题)【合集】

目录&#x1f60b; <第1关&#xff1a;顺序结构的应用> 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果&#xff1a; <第2关&#xff1a;交换变量值> 任务描述 相关知识 编程要求 测试说明 我的通关代码: 测试结果&#xff1a; <第…

git stash 的文件如何找回

在Git中&#xff0c;如果你使用了git stash命令来保存你的工作进度&#xff0c;但之后想要找回这些被stash的文件&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 查看stash列表 首先&#xff0c;使用git stash list命令来查看当前保存的所有stash记录。这个命令会列出…

【报表查询】.NET开源ORM框架 SqlSugar 系列

文章目录 前言实践一、按月统计没有为0实践二、 统计某月每天的数量实践三、对象和表随意JOIN实践四、 List<int>和表随意JOIN实践五、大数据处理实践六、每10分钟统计Count实践七、 每个ID都要对应时间总结 前言 在我们实际开发场景中&#xff0c;报表是最常见的功能&a…

基于asp.net游乐园管理系统设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php python(flask Django) 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找…

电脑上怎么运行手机APP(电脑上运行手机APP的4种方法)

玩家可能不愿意在小屏幕上使用 Android 应用程序。此外&#xff0c;某些游戏玩家可能更喜欢在更大的屏幕上寻找刺激的体验&#xff0c;例如 PC 提供的体验&#xff0c;这可以增强他们的乐趣。因此&#xff0c;他们可能对如何在 PC 上流畅地运行 Android 应用程序感到好奇。本指…

oscp学习之路,Kioptix Level1靶场小白速通教程

oscp学习之路&#xff0c;Kioptix Level1靶场小白速通教程 有意向学习oscp的可以私信我一起交流学习 1.前期准备 首先下载好靶场文件&#xff0c;通过nmap进行扫描&#xff0c;确定IP地址和开放端口 发现IP是192.168.201.129,开放的端口为22,80,111,139,443,1024这几个端口。访…

【Ubuntu】设置静态Ip

查看网卡 ifconfig | grep -A 5 -B 2 192 修改配置文件 sudo vim /etc/netplan/00-installer-config.yamlnetwork:ethernets:enp0s3:dhcp4: noaddresses:- 192.168.1.100/24gateway4: 192.168.1.1nameservers:addresses:- 8.8.8.8- 8.8.4.4​​​​​

演员念真主演电视剧《依依向北风》获奖

2024年12月17日&#xff0c;南京广播电视集团和江苏12家城市台的领导、嘉宾&#xff0c;全国其他省市电视台嘉宾&#xff0c;影视公司代表、项目主创团队代表等&#xff0c;相聚南京国际青年会议酒店&#xff0c;举行江苏城市联合公司“城市之星”项目资源推介会暨2024年度“城…

Word使用分隔符实现页面部分分栏

文章目录 Word使用分隔符实现页面部分分栏分隔符使用页面设置 Word使用分隔符实现页面部分分栏 分隔符使用 word中的分隔符&#xff1a; 前面不分栏&#xff0c;后面分栏(或前面分栏&#xff0c;后面不分栏)&#xff0c;只需要在分隔位置处插入分隔符&#xff1a;“连续”即…

Sensapex 微纳米显微平台:高精度、低漂移、模块化的科研引擎

Sensapex微纳米显微平台以其卓越的技术性能&#xff0c;成为科研领域的重要工具。其高精度定位技术确保了实验结果的可靠性&#xff0c;低漂移设计适应了长时间复杂实验的需求。模块化设计为用户提供了灵活性&#xff0c;可根据不同实验需求自由组合。Sensapex微纳米显微平台广…

江协科技 OLED库 OLED_Print( )函数自动换行

void OLED_Printf(int16_t X, int16_t Y, uint8_t FontSize, char *format, ...) {char String[256]; //定义字符数组va_list arg; //定义可变参数列表数据类型的变量argva_start(arg, format); //从format开始&#xff0c;接收参数列表到arg变量vsprintf(Strin…