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

Linux 实践:匿名管道与 Shell 外壳程序完善

基于 C++ 实现支持管道符及重定向的 Shell 外壳程序。核心利用 fork 创建子进程、pipe 建立通信通道、dup2 重定向文件描述符。重点解决了 strtok 静态指针覆盖导致的解析错误,区分了内置命令在父进程执行与外部命令在子进程执行的逻辑。代码封装了指令结构体,处理多命令链式调用及优先级更高的文件重定向,深入展示了 Linux 进程间通信机制。

极客零度发布于 2026/3/21更新于 2026/10/1987 浏览
Linux 实践:匿名管道与 Shell 外壳程序完善

内容回顾

上一节我们探讨了父子进程间通过匿名管道进行通信的原理。管道作为特殊的内存级文件,仅支持单向读写。为避免数据混乱,通常由一个进程写入,另一个进程读取,双方需正确关闭不用的读写端。

Linux 的匿名管道指令

Linux 中常见的 cmd1 | cmd2 语法本质就是匿名管道。Shell 解释器通过 fork 创建子进程,并在 fork 前调用 pipe 创建管道。子进程继承文件描述表后,标准输出被重定向至管道写端,标准输入被重定向至读端,从而实现进程间的数据流传递。

完善 Shell 外壳程序

整体框架

实现支持管道的 Shell 主要包含四个模块:获取用户输入、解析字符串、判断指令类型、执行指令。

1. 获取用户输入的字符串

由于输入可能包含空格和管道符,scanf 会在空格处截断,因此必须使用 fgets 读取整行。

char temp[MAX_SIZE];
if(fgets(temp,sizeof(temp),stdin)==NULL){
    perror("fgets");
    continue;
}
2. 解析字符串

我们需要统计基本指令个数,并分割指令部分、参数部分以及重定向信息。为此定义了一个 order 类来封装指令属性。

class order{
public:
    char* _argv[MAX_SIZE];
    int _argc=0;
    int _check_redir=0;
    char* filename=nullptr;
};

解析流程分为两步:先用 strtok 以 | 分割出所有基本指令存入临时数组,再逐个解析每个指令的参数。这里有一个关键陷阱:不能边分割边解析。因为 strtok 内部维护静态指针,若在 pipe_string 中调用 strtok 后,立即在 get_string 中再次调用 strtok,会覆盖之前的指针位置,导致后续指令丢失。

int pipe_string(std::vector<order>& order_array,char temp[]){
    int cmdnum=0;
    char* tmp[MAX_SIZE];
    char* token=strtok(temp,"|");
    while(token!=NULL&&cmdnum<MAX_SIZE){ 
        tmp[cmdnum]=token; 
        cmdnum++; 
        token=strtok(NULL,"|");
    }
    for(int i=0;i<cmdnum;i++){
        get_string(order_array[i],tmp[i]);
    }
    return cmdnum;
}

在 get_string 中,以空格分割参数,并检查末尾是否有重定向符号(>, >>, <)。注意重定向符号及其后的文件名不属于有效指令参数,需置为 NULL,但需先保存文件名到 filename 成员。

3. 判断指令个数

若指令数为 0,提示重新输入;若为 1,需判断是否为内置指令(如 cd, pwd)。内置指令修改的是当前 Shell 环境(如工作目录),必须在父进程中执行,否则子进程的修改对父进程无效。

const char* in_cmd[]={"cd","pwd",NULL};
bool check_order(order& a){
    for(int i=0;in_cmd[i]!=NULL;i++){
        if(strcmp(a._argv[0],in_cmd[i])==0){return true;}
    }
    return false;
}
4. 指令的执行

当指令数大于 1 时,涉及管道通信。需创建 n-1 个管道,并启动 n 个子进程。

  • 起始进程:只写管道,不读。
  • 结束进程:只读管道,不写。
  • 中间进程:读上一个管道的读端,写下一个管道的写端。
  • 重定向优先级:文件重定向优先级高于管道。例如 cmd1 >> log.txt | cmd2,cmd1 的输出应写入 log.txt 而非管道。

父进程负责关闭所有未使用的管道端,并等待子进程结束。

void pipe_order_complete(int cmdnum, std::vector<order>& order_array, std::vector<_pipe>& pipe_array){
    // 1. 创建所有管道
    for(int i =0; i < cmdnum -1; i++){
        int n=pipe(pipe_array[i].pipe_fd);
        if(n<0){perror("pipe");exit(EXIT_FAILURE);}
    }
    // 2. 创建子进程处理每个命令
    for(int i =0; i < cmdnum; i++){
        int pid =fork();
        if(pid <0){perror("fork");exit(EXIT_FAILURE);}
        if(pid ==0){
            // 子进程逻辑:处理重定向或管道连接
            execvp(order_array[i]._argv[0], order_array[i]._argv);
        }
    }
    // 3. 父进程关闭管道并等待
    for(int i =0; i < cmdnum -1; i++){
        close(pipe_array[i].pipe_fd[0]);
        close(pipe_array[i].pipe_fd[1]);
    }
    for(int i =0; i < cmdnum; i++){
        wait(NULL);
    }
}

完整实现

主要头文件 myshell.h 声明了数据结构与函数接口。myshell.cpp 中实现了主循环,不断读取用户输入并分发处理。

结语

通过上述步骤,我们实现了一个支持管道和基本重定向的简易 Shell。这加深了对进程间通信、文件描述符管理及系统调用的理解。实际开发中还需考虑更多边界情况,如信号处理、异步 IO 等。

目录

  1. 内容回顾
  2. Linux 的匿名管道指令
  3. 完善 Shell 外壳程序
  4. 整体框架
  5. 1. 获取用户输入的字符串
  6. 2. 解析字符串
  7. 3. 判断指令个数
  8. 4. 指令的执行
  9. 完整实现
  10. 结语

更多推荐文章

查看全部
  • Linux UDP 网络编程
  • Seedance 2.0 多模态 AI 视频创作操作手册
  • Android 开发核心知识点梳理与学习建议
  • Jupyter Notebook 中 pyecharts 无法显示可视化图形的解决方法
  • 医疗 AI 败血症预测:从数据到模型部署的 Python 全流程实战
  • 神经网络核心原理与入门指南
  • C++类型约束实战精要
  • LazyLLM 多 Agent 应用全流程实践:从源码部署到可视化 Web 调试的低代码方案
  • 赛博塔罗 Java 与前端实现方案
  • OpenClaw 开源汉化发行版:安装与配置指南
  • Stable Diffusion XL 1.0 灵感画廊镜像免配置部署指南
  • 用 Python 绘制可爱的招财猫
  • C++ 多态底层实现原理详解
  • Llama-2-7b在昇腾NPU上的六大核心场景性能基准报告
  • Kratos 主题 SMTP 配置:实现博客评论邮件通知
  • 数据库迁移 TCO 全景账本:MySQL 替代中的隐性成本与工程化工具链实测
  • 零基础黑客入门:普通人成为安全专家的十个步骤
  • Python 宠物美容服务预约档案系统设计与可视化
  • 面向中职学校的第二课堂教学管理系统设计
  • C++入门:输入输出、缺省参数与函数重载详解

相关免费在线工具

  • 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

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online