Shell自定义(二)
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;
}
相关文章:
Shell自定义(二)
1.Shell自定义 1.初始化 定义全局变量environ,把g_env的内容用memset初始化为0,这里用malloc开辟的空间为对应环境变量的长度1,多1位置是最后结束符0,strcpy把此时的对应的环境变量拷贝到g_env里面,下面是新增一个环…...
自然语言处理:我的学习心得与笔记
Pytorch 1.Pytorch基本语法 1.1 认识Pytorch 1.2 Pytorch中的autograd 2.Pytorch初步应用 2.1 使用Pytorch构建一个神经网络 2.2 使用Pytorch构建一个分类器 小节总结 学习了什么是Pytorch. 。Pytorch是一个基于Numpy的科学计算包,作为Numpy的替代者,向用户提供使用GPU强大…...
Oracle 中什么情况下 可以使用 EXISTS 替代 IN 提高查询效率
为什么 EXISTS 更高效? EXISTS 提前终止: EXISTS 一旦在子查询中找到第一个匹配项,就会立即返回 TRUE,不再继续扫描子查询中的其他记录。IN 必须扫描整个子查询的结果集,将所有结果与主查询的每一行进行对比。大数据集…...
Spring基础分析08-集成JPA/Hibernate进行ORM操作
大家好,今天和大家一起分享一下Spring集成JPAHibernate进行ORM操作的流程~ JPA(Java Persistence API)作为Java EE标准的一部分,提供了统一的API来管理实体类和持久化上下文;Hibernate则是最流行的JPA实现之一&#x…...
MySQL知识汇总(一)
一些命令行操作注意加 分号 “ ; ” show databases 查看所有数据库 use 数据库名 切换数据库 show tables 查看数据库中所有表 describe 表名 显示表中所有信息 create database [if not exists] 新库名 创…...
PDFMathTranslate 一个基于AI优秀的PDF论文翻译工具
PDFMathTranslate 是一个设想中的工具,旨在翻译PDF文档中的数学内容。以下是这个工具的主要特点和使用方法: 链接:https://www.modelscope.cn/studios/AI-ModelScope/PDFMathTranslate 功能特点 数学公式识别:利用先进的OCR&…...
React+Vite从零搭建项目及配置详解
相信很多React初学者第一次搭建自己的项目,搭建时会无从下手,本篇适合快速实现功能,熟悉React项目搭建流程。 目录 一、创建项目react-item 二、调整项目目录结构 三、使用scss预处理器 四、组件库Ant Design 五、配置基础路由 六、配置…...
@pytest.fixture() 跟 @pytest.fixture有区别吗?
在iOS UI 自动化工程里面最早我用的是pytest.fixture(),因为在pycharm中联想出来的fixture是带()的,后来偶然一次我没有带()发现也没有问题,于是详细查了一下pytest.fixture() 和 pytest.fixtur…...
Google Cloud Architect 认证考试错题集5
Google Cloud Architect 认证考试错题集5 D. Store static content such as HTML and images in a Cloud Storage bucket. Use Cloud Functions to host the APIs and save the user data in Firestore. - Storing static content in a Cloud Storage bucket is a cost-effecti…...
【Maven】基础(一)
【Maven】基础一 1. 虽然工作有段时间了,但是深感maven了解的不深入,所以这次开始深入的学习。 课程地址: https://www.bilibili.com/video/BV1JN411G7gX?spm_id_from333.788.player.switch&vd_source240d9002f7c7e3da63cd9a975639409a&p2 1.…...
多模态抽取图片信息的 Prompt
多模态抽取图片信息的 Prompt 1. 中文版2. 日文版3. 英文原版 下面使用多模态从图片中抽取文章,表格,Flowcharts的Prompt。 1. 中文版 你是一位擅长提取图片、图表、文本并对其进行解释的专家,能够保持原始语言不变。## 指南- 针对输入内容…...
WPF 使用LibVLCSharp.WPF实现视频播放、停止、暂停功能
使用LibVLCSharp.WPF实现视频播放、停止、暂停功能 1, NuGet 添加 VideoLAN.LibVLC.Windows 2. NuGet 添加 LibVLCSharp.WPF 3. wpf 代码如下: <Grid ><Grid.RowDefinitions><RowDefinition Height"*" /><RowDefinition Height&q…...
Java全栈项目 - 校园招聘信息平台
项目介绍 校园招聘信息平台是一个面向高校学生和企业的双向服务平台。该系统帮助企业发布招聘信息,方便学生查询职位并投递简历,同时为学校就业部门提供就业数据分析功能。 技术栈 后端 Spring Boot 2.xSpring SecurityMyBatis PlusMySQL 8.0RedisRabbitMQ 前端 Vue.js 2…...
java导出
请求头获取responseimport com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.write.metadata.WriteSheet;PostMapping("excel/export") ApiOperation(value "党员档案导出", httpMethod "POST")…...
【嵌入式系统】期末试题库,ARM处理器,CortexM3内核,USART,EXTI,GPIO
关注作者了解更多 我的其他CSDN专栏 过程控制系统 工程测试技术 虚拟仪器技术 可编程控制器 工业现场总线 数字图像处理 智能控制 传感器技术 嵌入式系统 复变函数与积分变换 单片机原理 线性代数 大学物理 热工与工程流体力学 数字信号处理 光电融合集成电路…...
arcgisPro相接多个面要素转出为完整独立线要素
1、使用【面转线】工具,并取消勾选“识别和存储面邻域信息”,如下: 2、得到的线要素,如下:...
QTday1
#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口标题this->setWindowTitle("向日葵远程控制");//设置窗口图标this->setWindowIcon(QIcon("C:\\Users\\Hasee\\Desktop\\pictrue\\mypicture\\logo.png&…...
SAP ALV选择列排序时弹出定义排序顺序窗口问题
需求场景 使用REUSE_ALV_GRID_DISPLAY_LVC生成ALV,发现一个问题:使用it_events的时候选择列排序时会弹出定义排序顺序窗口,如下图所示。(正常选择某一列再使用排序功能时会直接排序,不用再选择列) CLASS l…...
CSS Backgrounds(背景)
CSS Backgrounds(背景) Introduction(介绍) CSS backgrounds play a crucial role in web design, allowing developers to apply colors, images, and other decorative elements to the background of HTML elements. This enhances the visual appeal of web pages and he…...
欧拉计划 Project Euler 27 题解
欧拉计划 Problem 27 题解 题干思路code 题干 思路 可以先筛1e6的素数出来然后暴力找即可,具体思路看代码 code #include <bits/stdc.h>using namespace std;using ll long long;const int N 1e6 5; bool vis[N]; int pri[N];void getPrime() {memset(v…...
GPT-SoVITS WebUI 终极指南:5分钟快速上手一站式语音合成解决方案
GPT-SoVITS WebUI 终极指南:5分钟快速上手一站式语音合成解决方案 【免费下载链接】GPT-SoVITS 1 min voice data can also be used to train a good TTS model! (few shot voice cloning) 项目地址: https://gitcode.com/GitHub_Trending/gp/GPT-SoVITS GPT…...
PyTorch 3.0 DDP + torch.compile混合训练面试通关手册:涵盖Graph Break诊断、Shard策略冲突、以及3种反模式现场复现
第一章:PyTorch 3.0 静态图分布式训练面试概览PyTorch 3.0 并非官方发布版本(截至2024年,PyTorch最新稳定版为2.3),但“PyTorch 3.0”在面试语境中常被用作一种假设性技术命题,用于考察候选人对静态图编译、…...
Catalyst API 认证管理:处理 OAuth Token 失效问题
在使用 Catalyst API 进行数据操作时,OAuth Token 的管理是至关重要的。特别是当你尝试插入新记录到 Catalyst Datastore 表时,可能会遇到 “INVALID OAUTH TOKEN” 错误。本文将详细介绍如何有效地处理这一问题,并提供一个实际的示例来演示解决方案。 问题描述 在尝试使用…...
RWKV7-1.5B-g1a参数调优教程:temperature=0.1稳输出 vs 0.8活生成,效果差异实测
RWKV7-1.5B-g1a参数调优教程:temperature0.1稳输出 vs 0.8活生成,效果差异实测 1. 模型简介 rwkv7-1.5B-g1a是基于RWKV-7架构的多语言文本生成模型,特别适合以下场景: 基础问答文案续写简短总结轻量中文对话 这个1.5B参数的版…...
5块钱的国产RISC-V芯片CH32V103能干啥?我用它复刻了一个STM32F103的小项目
5元国产RISC-V芯片实战:用CH32V103复刻STM32经典项目 在电子DIY领域,成本始终是创客们无法回避的现实问题。当我在某电商平台发现CH32V103这颗标价仅5元的RISC-V芯片时,第一反应是怀疑它的实用性——毕竟同级别的STM32F103C8T6价格通常在15-2…...
Retinaface+CurricularFace模型在智能门禁系统中的实战应用
RetinafaceCurricularFace模型在智能门禁系统中的实战应用 1. 引言 想象一下这样的场景:每天早晨上班高峰期,办公楼入口排着长队等待刷卡进门;访客需要在前台登记身份证,保安还要手动核对信息。这种传统门禁方式不仅效率低下&am…...
ContextMenuManager:让Windows交互回归高效本质
ContextMenuManager:让Windows交互回归高效本质 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 当你在Windows系统中右键点击文件时,是否…...
基于物理信息神经网络的Burgers-Fisher方程求解方法研究(Python代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
车载Android Auto兼容性开发全链路(车规级Java SDK集成手册)
第一章:车载Android Auto兼容性开发全链路概览Android Auto 是 Google 提供的车载信息娱乐系统集成框架,其兼容性开发并非仅限于应用层适配,而是一条横跨设备端、车机系统、认证流程与用户交互的完整技术链路。开发者需同步关注 Android 应用…...
深入解析STM32 map文件:从编译到内存优化的关键步骤
1. 为什么STM32开发者必须掌握map文件分析 第一次接触STM32的map文件时,我和大多数新手一样感到一头雾水。这个由编译器自动生成的文本文件,乍看就像天书般难以理解。直到有次项目遇到内存不足的紧急情况,我才真正体会到map文件的价值——它不…...
