无法使用管道作为C中grep的输入

时间:2016-11-30 14:04:58

标签: c grep pipe fork dup2

在Ubuntu 16上,我正在尝试编写一个执行管道,分叉和执行的程序:

  • 程序将通过命令行参数接受文件名;
  • 子进程将打开指定文件并执行cat以将内容传输到第二个子进程;和
  • 第二个孩子将执行grep以选择包含转发到第三个子流程的数字的行
  • 第三个子进程打印收到的行。

这是我的代码:

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

#define BLOCK_SIZE 4096

int main(int argc, char** argv)
{
    int PID;
    int pipe1[2];
    int pipe2[2];
    int pipe3[2];
    char fileName[256];
    int lengthfileName = strlen(argv[1]);
    char content[BLOCK_SIZE];
    char modifiedContent[BLOCK_SIZE];
    int file;
    if(argc < 2)
    {
        printf("Usage prog file\n");
        exit(1);
    }
    if(pipe(pipe1) < 0)
    {
        printf("Error at pipe\n");
        exit(1);
    }
    if(pipe(pipe2) < 0)
    {
        printf("Error at pipe\n");
        exit(1);
    }
    if(pipe(pipe3) < 0)
    {
        printf("Error at pipe\n");
        exit(1);
    }
    if((PID = fork()) < 0)
    {
        printf("Error at process\n");
        exit(1);
    }


    if(PID == 0) //first child
    {
        close(pipe1[1]);
        read(pipe1[0],fileName,lengthfileName); 
        close(pipe1[0]);

        close(pipe2[0]); 
        dup2(pipe2[1],1);
        close(pipe2[1]); 
        execlp("/bin/cat","cat",fileName,NULL);


        exit(0);
    }
    else // parent
    {
        close(pipe1[0]); 
        write(pipe1[1],argv[1],lengthfileName);
        close(pipe1[1]);

        int status;

        if((PID = fork()) < 0)
        {
        printf("Error at process\n");
        exit(1);
        }
        if(PID == 0) // child 2
        {
            close(pipe2[1]);
            //read(pipe2[0],content,BLOCK_SIZE);
            //dup2(pipe2[0],0);// ***********************MARKED LINE HERE *****************************************
            close(pipe2[0]);    

            close(pipe3[0]);
            dup2(pipe3[1],1);
            close(pipe3[1]);    

            execlp("grep","grep","[0-9]",NULL);

            exit(0);

        }

        if((PID = fork()) < 0)
        {
            printf("Error at process\n");
            exit(1);
        }
        if(PID == 0) //cod fiu 2
        {
            close(pipe3[1]);
            read(pipe3[0],modifiedContent,BLOCK_SIZE);
            close(pipe3[0]);
            printf("GOT FROM PIPE:%s",modifiedContent);
            exit(0);
        }
        waitpid(PID, &status, 0);
    }

    return 0;
}

我的问题出在子进程2代码中,我尝试将管道用作grep的输入。如图所示,输入来自终端;如果我取消注释标记的行然后程序挂起,我必须手动杀死它以使其停止。

我如何使用pipe2向子进程2中的grep提供数据有什么问题?或者是其他地方的问题?

1 个答案:

答案 0 :(得分:0)

通过管道将文件名传输给第一个子节点有点傻,但依赖于从父节点继承其长度的子节点。如果你要继承名称的长度,那么你也可以继承整个文件名,省去第一个管道。

你可以想象首先在管道上发送(固定大小)长度值以避免继承它,但这样的方案毫无意义 - 分叉子进程不仅从父进程继承数据,你不能避免依赖于在你的程序中。特别是,子必须从父级继承打开的管道末端和管道文件描述符数组,以便单亲方法可以工作。

另请注意,您(或许)幸运地通过管道收到的文件名空终止。第一个孩子既不从管道中读取它也不明确地设置它。

但主要问题似乎是你有流浪的管道末端。在分叉任何子项之前,您在父项中创建所有三个管道。因此,在每个分支处,子项将继承所有管道末端的打开文件描述符,而父级尚未关闭。子进程应该关闭它们不使用的所有打开的管道末端,但它们只关闭其中的一些。 grep(和cat)等程序在看到文件末尾之前不会退出,并且当任何进程成立时,它们不会在管道上看到它写结束打开。

具体来说,父进程永远不会关闭pipe2的写结束,实际上第三个子进程继承该开放描述符并且也不会关闭它。第一个孩子在退出时关闭该FD的副本,但在管道末端打开另外两个手柄时,该末端保持打开状态。因此,当第二个孩子从该管道获取其输入时,它永远不会看到文件结束,并且永远不会退出。让父母关闭pipe2的两端,分叉第二个孩子并分叉第三个孩子应该解决这个问题。