跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 与 Linux 基础:用 C++ 实现简易 Shell 进程控制

C++ 实现简易 Shell,涵盖 exec 系列函数详解(execl, execv, execvp, execvpe)、用户指令获取、子进程创建与执行。通过 fork 和 exec 替换进程映像,实现命令行解析与命令运行功能。

MongoKing发布于 2026/2/25更新于 2026/5/2526 浏览
C++ 与 Linux 基础:用 C++ 实现简易 Shell 进程控制

1.知识准备

1-0 小前言

在 Linux 世界里,Shell 是用户最熟悉的程序之一。它既不是黑魔法,也不是操作系统本身,只是一个运行在用户态的程序。它的工作像一个传声筒:读取指令 -> 翻译给内核 -> 把结果拿回来。今天,我们用 C++ 从零实现一个简单的 Shell。

1-1 Shell 是什么

Shell 的中文意思是'壳'。想象一下坚果或者鸡蛋:

  1. 核心(Kernel):坚果的果肉。这是操作系统最核心的部分,直接管理 CPU、内存等硬件。
  2. 外壳(Shell):坚果的硬壳。包裹在核心外面,保护核心。
  3. 用户(User):我们在壳外面。 结论:Shell 就是包裹在操作系统内核外面的一层软件壳。它是用户和内核之间的翻译官。

1-2 必备的 exec 系列函数

exec 系列函数用于替换当前进程的映像。如果说 fork() 是分身术,那么 exec 系列函数就是夺舍。当一个进程调用 exec 函数时,它的身体(PCB、进程 ID)还是原来的,但是灵魂(代码段、数据段、堆栈)被完全替换了。一句话总结:exec 只有去,没有回。

1-2-1 execl 函数
#include <stdio.h>
#include <unistd.h>

int main() {
    printf("这是我的进程\n");
    // 参数列表以 NULL 结尾
    execl("/bin/ls", "ls", "-a", "-l", NULL);
    // 如果 execl 成功,下面这行永远不会执行
    perror("execl failed");
    return 0;
}
1-2-2 execv 函数

v 代表数组。我们需要把自己需要的指令放在一个数组里面。

#include <unistd.h>
#include <stdio.h>

int main() {
    printf("Before exec\n");
    char* args[] = {"ls", "-l", "-a", NULL};
    int ret = execv("/bin/ls", args);
    perror("execv failed");
    return 1;
}
1-2-3 execvp 函数

尾部加一个 p,表示在 PATH 环境变量中查找程序,不需要写完整路径。

#include <unistd.h>
#include <stdio.h>

int main() {
    char* args[] = {"ls", "-l", NULL};
    printf("尝试运行 ls ...\n");
    if (execvp("ls", args) == -1) {
        perror("execvp 失败");
        return 1;
    }
    return 0;
}
1-2-4 execvpe 函数

e 代表 Environment,可以传入自定义环境变量数组。

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>

int main() {
    char* args[] = {"printenv", NULL};
    char* new_env[] = {"MY_NAME=WWH", "HOME=/tmp", NULL};
    printf("正在运行 printenv,并替换环境变量...\n");
    execvpe("printenv", args, new_env);
    perror("execvpe failed");
    return 1;
}
1-2-5 总结
后缀含义作用
lList参数一个个列出来
vVector参数放数组里
pPath在 $PATH 里找程序
eEnvironment自定义环境变量

2.主要功能 1:打出指定的格式

2-1 三个 Get 函数,获取信息

利用 getenv 获取环境变量。

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

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

const char* GetPwd() {
    char* name = getenv("PWD");
    return name == nullptr ? "None" : name;
}

2-2 打印 shell,启动进程

void PrintCommand() {
    printf("[%s@%s %s]* ", GetUserName(), GetHostName(), GetPwd());
    fflush(stdout);
}

Makefile:

MyShell: MyShell.cc
g++ -o $@ $^ -std=c++11
.PHONY: clean
clean:
rm -f MyShell

2-3 开始改进代码,复用性提高

void MakeCommand(char* cwd_prompt, int size) {
    snprintf(cwd_prompt, size, "%s@%s %s* ", GetUserName(), GetHostName(), GetPwd());
}

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

3.主要功能 2:获取用户指令

使用 fgets 从键盘读取,并用 strtok 分割命令。

bool GetCommandLine(char* out, int size) {
    char* c = fgets(out, size, stdin);
    if (c == nullptr) return false;
    out[strlen(out) - 1] = 0; // 去掉换行符
    if (strlen(out) == 0) return false;
    return true;
}

#define SPE " "
bool CommandParse(char* command) {
    g_argc = 0;
    g_argv[g_argc++] = strtok(command, SPE);
    while ((bool)(g_argv[g_argc++] = strtok(nullptr, SPE)));
    g_argc--;
    return true;
}

4.主要功能 3:利用子进程执行

int Execute() {
    pid_t id = fork();
    if (id == 0) {
        // child
        execvp(g_argv[0], g_argv);
        exit(1);
    }
    // father wait
    pid_t rid = waitpid(id, nullptr, 0);
    (void)rid;
    return 0;
}

int main() {
    while (true) {
        PrintCommand();
        char commandline[COMMAND_SIZE];
        GetCommandLine(commandline, sizeof(commandline));
        CommandParse(commandline);
        Execute();
    }
    return 0;
}

总结

这个 Shell 实现了基本的命令解析和执行功能,主要问题在于 cd 等内置命令的处理。通过 fork 和 exec 系列函数,我们完成了对简易 Shell 工具的开发。

目录

  1. 1.知识准备
  2. 1-0 小前言
  3. 1-1 Shell 是什么
  4. 1-2 必备的 exec 系列函数
  5. 1-2-1 execl 函数
  6. 1-2-2 execv 函数
  7. 1-2-3 execvp 函数
  8. 1-2-4 execvpe 函数
  9. 1-2-5 总结
  10. 2.主要功能 1:打出指定的格式
  11. 2-1 三个 Get 函数,获取信息
  12. 2-2 打印 shell,启动进程
  13. 2-3 开始改进代码,复用性提高
  14. 3.主要功能 2:获取用户指令
  15. 4.主要功能 3:利用子进程执行
  16. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AXI 总线详解与 FPGA 实现实战指南
  • 前端 GraphQL 客户端:高效获取数据的最佳实践
  • 做一名黑客需要哪些技能?入门知识详解
  • 八种常见排序算法详解
  • 云环境训练 Stable Diffusion LoRA 模型教程
  • 转行 AI 大模型:码农与产品经理的职业机遇与挑战
  • 消息队列理论基础与 Kafka 架构价值解析
  • 移动云算联网助力物流网络智能化升级方案
  • Python 图像差异分析工具 diffimg 使用指南
  • SAP与Java系统WebService交互实践
  • Linux 基础 I/O 原理与系统调用
  • KMP 算法详解:利用失配信息优化字符串匹配
  • 动态规划:斐波那契数列模型及常见变体
  • OpenClaw 开源 AI 智能体框架技术解析与部署实践
  • Google 发布 2020 图像匹配基准与挑战赛
  • OpenVLA 模型微调及机器人端部署实战
  • MCP 协议详解:AI 智能体连接外部工具的新标准
  • 从词向量到多模态嵌入:大型语言模型技术、应用及未来方向
  • 科学家证实高脂饮食致肥胖可能促进癌细胞生长
  • AIGC 技术解析:市场现状、挑战与实战代码示例

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online