使用双管道实现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
execvpreplaces the parent process before forking the second child: In your parent process block (pid > 0), you callexecvp(exp[0], exp)immediately. This replaces the parent's entire address space with thegrepprocess—so the code after that (forkingpid2, 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
catto the firstgrep, and the second pipe should carry output from the firstgrepto the secondgrep. 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
- Correct process creation order: We first fork the
catchild, then fork the firstgrepchild, and the parent runs the finalgrep. This ensures all three processes are created and connected properly. - Proper pipe redirection:
catsends its output to the first pipe's write end (fd[1]).- First
grepreads from the first pipe's read end (fd[0]) and writes to the second pipe's write end (fd2[1]). - Second
grepreads from the second pipe's read end (fd2[0]) and writes to the terminal (default stdout).
- 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.
- Error handling: Added
perrorcalls to help debug if anypipe,fork, orexecvpcalls fail.
How to Test
- Make sure
filename.txtexists and contains some instances of "stringname". - Compile with
gcc pipeline.c -o pipeline. - Run with
./pipeline—you should see the count of lines that contain "stringname" as whole words (matching the shell pipeline result).
内容的提问来源于stack exchange,提问作者Sergio
相关产品推荐
相关产品推荐

