Shell自定义(二)

embedded/2024/12/26 17:51:37/

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_testg_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 = 1if(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 $PATHstd::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_testg_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 = 1if(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 $PATHstd::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; // 清理\nif(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){//childexecvp(g_argv[0], g_argv);exit(1);}int status = 0;// fatherpid_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.ppmy.cn/embedded/148397.html

相关文章

vue3入门教程:组件v-model

一、v-model的基本工作原理 v-model在Vue中主要用于实现数据的双向绑定&#xff0c;即当表单元素或组件的输入值发生变化时&#xff0c;会自动更新绑定的数据属性&#xff1b;反之&#xff0c;当数据属性发生变化时&#xff0c;也会自动更新表单元素或组件的显示。 二、在组合…

解析基于 SSM 框架 Vue 电脑测评系统:把握电脑测评精髓

摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;作为一个一般的用户都开始注重与自己的信息展示平台&#xff0c;实现基于SSM框架的电脑测评系统在技术上已成熟。本文介绍了基于SSM框架的电脑测评系统的开发全过程。通过分析用户对于基于SSM框架的电脑测评系统的…

leetcode之hot100---142环形链表II(C++)

思路一&#xff1a;哈希表 参考环形链表的思路一&#xff0c;当在哈希表中找到第一个表外待入表的节点&#xff0c;说明链表存在环&#xff0c;且该节点为入环的第一个节点 class Solution { public:ListNode *detectCycle(ListNode *head) {unordered_set<ListNode *>…

elasticsearch中使用fuzzy查询

文章目录 1. fuzzy 查询的基本用法示例文档&#xff1a; 2. 基本的 fuzzy 查询解释&#xff1a;查询结果&#xff1a; 3. fuzziness 的不同设置**fuzziness 设置为数字&#xff08;编辑距离&#xff09;**fuzziness 设置为 0 4. 更多的 fuzzy 查询选项示例&#xff1a; 5. 总结…

视频矩阵系统怎么做?深度解析矩阵全链路玩法

今天咱们聊聊&#xff0c;为什么企业做短视频一定要搞矩阵&#xff1f;还&#xff0c;这个矩阵号到底应该怎么打造&#xff1f; 什么是抖音矩阵账号&#xff1f;简单来说&#xff0c;就是在一个品牌或企业在抖音平台上创建并管理多个员工账号&#xff0c;这些账号都和主账号有…

Windows查看MD5

如何在Windows&#xff0c;查看一个文件的MD5 1、ctrlr&#xff0c;输入cmd 2、执行命令certutil -hashfile 文件路径&#xff08;按住将文件拖进来就行&#xff09; MD5 3、执行命令certutil -hashfile 文件路径&#xff08;按住将文件拖进来就行&#xff09;SHA1 可查看SHA…

Gaea学习笔记总结

Gaea 是一款地形创建软件&#xff0c;它内置了丰富的地貌节点&#xff0c;能快速生成像山脉、荒原峡谷、河流、湖泊等地貌特征。 节点解释使用方法概述Primitives&#xff08;基本体&#xff09;Constant&#xff08;常数&#xff09;创建输出&#xff0c;一般用来输出Hight&am…

flux模型的下载、配套及简易使用记录(ubuntu)

我在学习使用时&#xff0c;很迷惘各个模型放在什么 位置。以及他们的作用。所以系统的了解了一下。然后记录了&#xff0c;一下&#xff0c;希望能帮助到想了解这个知识的朋友。 另外&#xff0c;我将持续的更新这个专辑。记录我在学习和使用过程中关于comfy的方面。希望得到…