You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C程序中实现与grep进程的输入输出交互?

问题分析与解决

核心问题:管道缓冲与未关闭的文件描述符

你的代码中grep没有输出的原因有两个关键因素:

1. grep的全缓冲策略

当程序的标准输出不是终端时(比如管道),grep会采用全缓冲模式:只有当输出缓冲被填满、程序主动调用fflush,或者程序退出时,才会将缓冲内容写入管道。

  • 你的代码中,grep匹配到的内容只有7字节(apples\n),远小于默认的4KB缓冲大小,所以不会主动刷新。
  • 但grep本应在读完输入(父进程关闭main2grep.write后,grep的stdin收到EOF)后退出,退出时自动刷新缓冲。问题出在第二个因素:

2. 未关闭的管道写端文件描述符

父进程创建grep2main管道后,一直保留着grep2main.write的打开状态。而管道的规则是:只有当所有写端文件描述符都关闭时,读端的read才会返回EOF。

  • 即使grep退出并刷新了缓冲内容到管道,父进程的read在读完数据后会一直阻塞(因为写端还有父进程自己的grep2main.write打开着)。同时子进程在execvp前未清理多余的管道fd,进一步干扰了管道的正常状态。

代码修复方案

修改两处即可解决问题:

  1. 子进程中关闭多余的管道文件描述符:在execvp前,关闭不需要的管道读/写端,避免残留的fd影响管道状态。
  2. 父进程关闭grep2main.write:父进程只需要从该管道读,不需要写,关闭写端让管道状态正确。

修复后的代码:

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

struct Pipe
{
    int read;
    int write;
};

struct Pipe main2grep;
struct Pipe grep2main;

int main()
{
    pipe((int*) &main2grep);
    pipe((int*) &grep2main);

    char *arguments[4];
    arguments[0] = "grep";
    arguments[1] = "apples";
    arguments[2] = 0;

    int result = fork();
    if (result == 0)
    {
        dup2(main2grep.read, STDIN_FILENO);
        dup2(grep2main.write, STDOUT_FILENO);

        // 关闭多余的管道fd
        close(main2grep.read);
        close(main2grep.write);
        close(grep2main.read);
        close(grep2main.write);

        execvp(arguments[0], arguments);
        return 0;
    }

    // 父进程关闭不需要的管道fd
    close(main2grep.read);
    close(grep2main.write);

    char *input = "apples\n";
    int write_result = write(main2grep.write, input, 7);
    printf("wrote %d bytes to %s\n", write_result, arguments[0]);
    close(main2grep.write);
    
    char buffer[128];
    while (1)
    {
        int n_read = read(grep2main.read, buffer, 128);
        if (n_read == -1) {
            perror("read error");
            break;
        } else if (n_read == 0) {
            // 所有写端关闭,读到EOF,退出循环
            printf("grep exited, no more data\n");
            break;
        }
        buffer[n_read] = 0;
        printf("from grep: %s\n", buffer);
    }

    return 0;
}

关于命令行echo hello | grep hello | cat的疑问

grep并不会误以为cat是终端。grep通过isatty(STDOUT_FILENO)可以明确检测到stdout是管道(不是终端),所以依然用全缓冲。

  • 命令行中echo输出一行数据后就退出,grep读完stdin的EOF后处理匹配行,随后立即退出。程序退出时会强制刷新所有标准IO缓冲,所以grep的输出会被写入管道,cat读到后输出到终端,因此你能看到结果。

内容的提问来源于stack exchange,提问作者Jack M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:56:07