Shell进程替换,自定义Shell解释器——字符串库函数灵活操作!

@bit::Shadow
✧(≖ ◡ ≖✿

目录

进程程序替换

"程序"怎么理解?

shell内程序替换的接口

6+1

命名规律:

第一个参数path/file

which与where查询位置

自定义shell命令解释器

0.设置环境变量

1.命令行提示符

2.获取用户输入

strtok()字符串截取函数

例:

执行结果:

myshell模块功能实现:

3.指令执行(可优化封装)

strcmp()

strcpy()

dest要求:

strcat()

☆☆☆snprintf()

参数

返回值:

使用实例:

myshell指令执行功能


进程程序替换

定义:进程程序替换指使用一个新的程序 (可执行文件),替换当前进程的代码数据 栈 堆 内存段等 ,但保留 原进程的PID、某些环境及文件描述符等信息。替换完成后程序执行新的代码块,新代码块执行完成后,原代码块不再执行

"程序"怎么理解?

一个存储在硬盘上的,静态的、可执行的指令和数据的集合,它是一个/多个死的文件

shell内程序替换的接口

6+1

接口有6+1个前六个复用第七个,前六个作为库函数最后一个作为系统调用。

前六个:

后一个:

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/file

path:文件路径 ,使用这个路径查找可执行文件名,因此是完整的文件路径。

file:可以加路径也可以 不加路径,直接输入指令名称使得从环境变量中自动寻找指令但是要将后续的指令执行全部规范写出,不可因第一个指令而省略。

以execl()为例:

cpp 复制代码
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();

cpp 复制代码
//设置环境变量
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); // 参数为环境变量名称 返回对应内容

cpp 复制代码
//命令行
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()字符串截取函数

cpp 复制代码
#include <string.h>
char *strtok(char *str, const char *delim);

char* strtok(char* str, const char* delim); // 用于分割字符串,delim为分割单位

具体功能:将原串delim处变作'\0',并返回分割得到的子串。

若想要多次分割(对同一字符串),第一个参数必须传入NULL。

例:
cpp 复制代码
#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.txt

myshell模块功能实现:
cpp 复制代码
//指令解析
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()

cpp 复制代码
#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()

cpp 复制代码
#include <string.h>
char *strcpy(char *dest, const char *src);

功能:将src复制到dest

dest要求:
要求 说明 违反后果
有效的内存地址 不能是 NULL 或野指针 段错误,程序崩溃
可写内存 不能是只读内存(如字符串常量) 段错误或未定义行为
足够的空间 至少 strlen(src) + 1 字节 缓冲区溢出,安全漏洞
已分配空间 不能是未初始化的指针 未定义行为,崩溃

strcat()

对dest要求可以找到'\0'。

cpp 复制代码
#include <string.h>
char *strcat(char *dest, const char *src);

☆☆☆snprintf()

作为字符串拷贝转移最安全的函数

cpp 复制代码
#include<stdio.h>
int snprintf(char* str, size_t size, const char* format, ...);
参数
  • str:目标缓冲区

  • size:缓冲区大小(字节数)

  • format:格式化字符串

  • ...:可变参数

返回值:
  • 成功时:返回应该写入的字符数(不包括'\0')。
  • 失败时:返回负数。

关键特性

  • 自动添加 '\0' 结尾

  • 不会溢出 (超过 size-1 的内容被截断)

  • 可替代 strcpystrcat

使用实例:
cpp 复制代码
#include <stdio.h>

int main() 
{
    char buffer[20];
    
    // 基本使用
    snprintf(buffer, sizeof(buffer), "Hello");
    printf("%s\n", buffer);  // Hello
}

myshell指令执行功能

cpp 复制代码
//指令执行
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::cout<<s;
        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);   
}

感谢观看

多多关注

相关推荐
sitellla2 分钟前
Pydub:用 Python 处理音频,不写废话
开发语言·python·其他·音视频
LT10157974443 分钟前
2026年UI自动化测试平台选型指南:全界面自动化覆盖方案
运维·ui·自动化
xingyuzhisuan10 分钟前
缓存命中率提升方案:从 30% 优化至 82% 全流程优化记录
java·开发语言·缓存·ai
TechWayfarer12 分钟前
云服务器地域怎么选:用离线IP数据库识别用户来源并优化部署
服务器·数据库·python·tcp/ip·数据分析
郑洁文16 分钟前
基于Python的恶意流量监测系统的设计与实现
开发语言·python
AI玫瑰助手18 分钟前
Python流程控制:for循环与range函数的搭配使用
开发语言·python·信息可视化
red_redemption21 分钟前
自由学习记录(201)
学习
一条泥憨鱼21 分钟前
Java开发效率神器:Lombok从入门到精通!
java·后端·学习·开发·lombok
anew___21 分钟前
2026年Python爬虫技术完全指南:从入门到实战
开发语言·爬虫·python
Penfy_Z23 分钟前
【Python LLM 调用踩坑】Connection error 终极解决方案!npm 代理导致阿里云通义千问接口连接失败
开发语言·python·npm