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

使用双管道实现cat|grep|grep的C程序执行异常求助

排查并修复管道实现cat|grep|grep时的卡住问题

Let's break down why your code is hanging and fix it step by step. The goal is to replicate the shell pipeline cat filename.txt | grep -w "stringname" | grep -c "stringname", but your current implementation has several critical flaws with process execution order, pipe connections, and file descriptor management.

Key Issues in Your Code

  • execvp replaces the parent process before forking the second child: In your parent process block (pid > 0), you call execvp(exp[0], exp) immediately. This replaces the parent's entire address space with the grep process—so the code after that (forking pid2, setting up the second grep) never runs at all. That's why your pipeline is incomplete and hangs.
  • Pipe connections are inverted and misassigned: You're trying to connect the pipes in the wrong order. The first pipe should carry output from cat to the first grep, and the second pipe should carry output from the first grep to the second grep. Your current file descriptor duplication has the parent pointing to the wrong pipe ends.
  • Unclosed pipe file descriptors: Every process must close all pipe ends it doesn't use. If a pipe's write end isn't closed by all processes, the reading process will wait forever for more data (since the kernel thinks there might still be a writer).

Fixed Code Implementation

Here's the corrected code that properly implements the three-stage pipeline:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main() {
    pid_t pid1, pid2;
    int fd[2], fd2[2];

    // Create two pipes: fd connects cat -> grep1; fd2 connects grep1 -> grep2
    if (pipe(fd) == -1 || pipe(fd2) == -1) {
        perror("pipe failed");
        exit(EXIT_FAILURE);
    }

    // First fork: run cat command
    pid1 = fork();
    if (pid1 == 0) {
        // Redirect stdout to fd[1] (write end of first pipe)
        close(STDOUT_FILENO);
        dup(fd[1]);
        // Close all unused pipe ends
        close(fd[0]);
        close(fd[1]);
        close(fd2[0]);
        close(fd2[1]);

        // Execute cat filename.txt
        char *exp[] = {"cat", "filename.txt", NULL};
        execvp(exp[0], exp);
        perror("execvp cat failed"); // Only runs if execvp fails
        exit(EXIT_FAILURE);
    } else if (pid1 < 0) {
        perror("fork cat failed");
        exit(EXIT_FAILURE);
    }

    // Second fork: run first grep (-w stringname)
    pid2 = fork();
    if (pid2 == 0) {
        // Redirect stdin to fd[0] (read end of first pipe)
        close(STDIN_FILENO);
        dup(fd[0]);
        // Redirect stdout to fd2[1] (write end of second pipe)
        close(STDOUT_FILENO);
        dup(fd2[1]);
        // Close all unused pipe ends
        close(fd[0]);
        close(fd[1]);
        close(fd2[0]);
        close(fd2[1]);

        // Execute grep -w stringname
        char *exp[] = {"grep", "-w", "stringname", NULL};
        execvp(exp[0], exp);
        perror("execvp grep1 failed");
        exit(EXIT_FAILURE);
    } else if (pid2 < 0) {
        perror("fork grep1 failed");
        exit(EXIT_FAILURE);
    }

    // Parent process runs the second grep (-c stringname)
    // Redirect stdin to fd2[0] (read end of second pipe)
    close(STDIN_FILENO);
    dup(fd2[0]);
    // Close all unused pipe ends
    close(fd[0]);
    close(fd[1]);
    close(fd2[0]);
    close(fd2[1]);

    // Execute grep -c stringname (output goes to terminal by default)
    char *exp2[] = {"grep", "-c", "stringname", NULL};
    execvp(exp2[0], exp2);
    perror("execvp grep2 failed");
    exit(EXIT_FAILURE);

    // Wait for child processes (though execvp replaces parent, so this is just for safety)
    wait(NULL);
    wait(NULL);
    return 0;
}

Explanation of the Fixes

  1. Correct process creation order: We first fork the cat child, then fork the first grep child, and the parent runs the final grep. This ensures all three processes are created and connected properly.
  2. Proper pipe redirection:
    • cat sends its output to the first pipe's write end (fd[1]).
    • First grep reads from the first pipe's read end (fd[0]) and writes to the second pipe's write end (fd2[1]).
    • Second grep reads from the second pipe's read end (fd2[0]) and writes to the terminal (default stdout).
  3. Full pipe descriptor cleanup: Every process closes all pipe ends it doesn't need. This is crucial to prevent the reading processes from hanging waiting for unused write ends to close.
  4. Error handling: Added perror calls to help debug if any pipe, fork, or execvp calls fail.

How to Test

  1. Make sure filename.txt exists and contains some instances of "stringname".
  2. Compile with gcc pipeline.c -o pipeline.
  3. Run with ./pipeline—you should see the count of lines that contain "stringname" as whole words (matching the shell pipeline result).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 09:07:41