Shell进程替换,自定义Shell解释器——字符串库函数灵活操作!
bit::Shadow✧(≖ ◡ ≖✿目录进程程序替换“程序”怎么理解shell内程序替换的接口61命名规律第一个参数path/filewhich与where查询位置自定义shell命令解释器0.设置环境变量1.命令行提示符2.获取用户输入strtok()字符串截取函数例执行结果myshell模块功能实现3.指令执行(可优化封装)strcmp()strcpy()dest要求strcat()☆☆☆snprintf()参数返回值使用实例myshell指令执行功能进程程序替换定义进程程序替换指使用一个新的程序可执行文件替换当前进程的代码数据 栈 堆 内存段等但保留原进程的PID、某些环境及文件描述符等信息。替换完成后程序执行新的代码块新代码块执行完成后原代码块不再执行。“程序”怎么理解一个存储在硬盘上的静态的、可执行的指令和数据的集合它是一个/多个死的文件。shell内程序替换的接口61接口有61个前六个复用第七个前六个作为库函数最后一个作为系统调用。前六个后一个:7者一旦返回-1代表执行失败。观察发现它们的组成方式都是“exec”…… 参数几乎是一致的实际上七者对应参数的功能都是一致的。命名规律l(list) vs v(vector)指明命令行参数的传递方式l :采用参数列表的形式。像execl(usr/bin/ls, ls, -l, NULL)这样每个参数作为单独的参数依次列出最后以NULL结尾。v:采用数组的方式。先将所有参数存储到一个指针数组内包含NULL然后把数组地址传递给函数。p(path)指明可执行文件的查找方式带p只需提供文件名如ls函数会在自动环境变量PATH中搜索。不带p必须提供完整的文件路径如/usr/bin/ls。e(environment)指明环境变量的来源带e可以传入一个envp[]数组来自定义新程序的环境变量。很少使用不带e新程序会默认继承当前进程的环境变量。第一个参数path/filepath文件路径使用这个路径查找可执行文件名因此是完整的文件路径。file可以加路径也可以不加路径直接输入指令名称使得从环境变量中自动寻找指令但是要将后续的指令执行全部规范写出不可因第一个指令而省略。以execl()为例int execl(const char*path, const char* arg0, ……, (char*)0);which与where查询位置where所有找到的匹配项包括别名、二进制、源码、帮助文档更全面适合查看命令的“所有存在形式”which只输出第一个匹配的二进制文件路径更简洁只告诉你会执行哪个第一个参数路径path的寻找由“l”推出参数列表运行运行结果file参数可直接写指令数组char* argv[]自定义shell命令解释器设计导图(图片过大 无法全部呈现-smlShell仓库)0.设置环境变量由于无法获取系统层面的环境变量只能通过const char** environ来拷贝putenv();//设置环境变量 char* enVr[129];//内部存储char*类型lll void EnvInit() { memset(enVr, 0, sizeof(enVr)); // 好的习惯 extern char** environ;//内部存储char**的数据类型environ[] lll for(int i 0;environ[i];i)//i至多为20 { enVr[i](char*)malloc(strlen(environ[i])1); //enVr[i](char*)malloc(sizeof(strlen(environ[i])1)); strcpy(enVr[i],environ[i]); putenv(enVr[i]); } }1.命令行提示符char* getenv(const char* EnvmVar); // 参数为环境变量名称返回对应内容//命令行 void CoutCommandLine() { std::cout [ getenv(USER) \ getenv(HOSTNAME) getenv(PWD) ]; if(strcmp(getenv(USER),root)0) std::cout # ;//err当相等时返回0 else std::cout $ ; }2.获取用户输入strtok()字符串截取函数#include string.h char *strtok(char *str, const char *delim);char* strtok(char* str, const char* delim); // 用于分割字符串delim为分割单位具体功能将原串delim处变作\0并返回分割得到的子串。若想要多次分割(对同一字符串)第一个参数必须传入NULL。例#include stdio.h #include string.h int main() { char path[] /home/user/documents/file.txt; // 分割路径 char *token strtok(path, /); while (token ! NULL) { printf(目录/文件: %s\n, token); token strtok(NULL, /); } return 0; }执行结果目录/文件: home目录/文件: user目录/文件: documents目录/文件: file.txtmyshell模块功能实现//指令解析 void CommandParsed() { //std::cout CommandLine ; //1.不接纳数组传参的直接淘汰 //2.file和path选file无需位置指定 //3.无需传环境变量所以选择 //int execvp(const char* file, char *const argv[]) //第一个命令的截取 若一个命令由多部分组成该怎么办怎么区分指令与选项 int i 0; CommandStr[i]strtok(CommandLine, );//自动将分隔符替换为\0 //截取剩余选项的主逻辑 while((bool)(CommandStr[i]strtok(NULL, ))); }3.指令执行(可优化封装)strcmp()#include string.h int strcmp(const char *str1, const char *str2);int strcmp(const char* str1, const char* str2);//字符串比较函数字符串相等返回0。 // 注意由于返回0所以if(!strcmp(str1, str2)) ;strcpy()#include string.h char *strcpy(char *dest, const char *src);功能将src复制到destdest要求要求说明违反后果有效的内存地址不能是NULL 或野指针段错误程序崩溃可写内存不能是只读内存如字符串常量段错误或未定义行为足够的空间至少strlen(src) 1字节缓冲区溢出安全漏洞已分配空间不能是未初始化的指针未定义行为崩溃strcat()对dest要求可以找到\0。#include string.h char *strcat(char *dest, const char *src);☆☆☆snprintf()作为字符串拷贝转移最安全的函数#includestdio.h int snprintf(char* str, size_t size, const char* format, ...);参数str目标缓冲区size缓冲区大小字节数format格式化字符串...可变参数返回值成功时返回应该写入的字符数不包括\0。失败时返回负数。关键特性✅自动添加\0结尾✅不会溢出超过size-1的内容被截断✅可替代strcpy和strcat使用实例#include stdio.h int main() { char buffer[20]; // 基本使用 snprintf(buffer, sizeof(buffer), Hello); printf(%s\n, buffer); // Hello }myshell指令执行功能//指令执行 void CommandRun() { //ls ls -a -l NULL if(strcmp(CommandStr[0],cd)0) { if(!strcmp(CommandStr[1],getenv(HOME))) strcpy(getenv(PWD),getenv(HOME)); else strcpy(getenv(PWD),CommandStr[1]); } else if(strcmp(CommandStr[0],echo)0) { //echo “字符串” 环境变量/本地变量 $? std::string s CommandStr[1]; //std::couts; if(s$?) { std::cout exitCode std::endl; } else if(s[0]$) { //CommandStr $PATH std::string s1 s.substr(1); std::cout getenv(s1.c_str()) std::endl; } else{ std::cout s std::endl; } } else if(strcmp(CommandStr[0],env)0) { for(int i 0;enVr[i];i) std::cout enVr[i] std::endl; std::cout std::endl std::endl; } else execvp(CommandStr[0],CommandStr); }感谢观看多多关注