C++ piping issue

2019-08-10 22:07发布

问题:

I am trying to to fork my c++ program and direct the parents output into the childs input, I am using pipe() and fork(). In the directory of the program there is a file called input.txt. Unfortunately the only output I get is "wc: stdin: read: Bad file descriptor". Does anyone know why this is? If so what am I doing wrong? Thanks

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

int main(int argc, char *argv[]){

        int pipes[2],pid,stdIn,stdOut;

        stdIn = dup(0);
        stdOut = dup(1);

        pipe(pipes);

        pid = fork();

        if(pid == 0){
                dup2(pipes[1],0);
                close(pipes[1]);
                execlp("wc","wc",NULL);
        }
        else{
                dup2(pipes[0],1);
                close(pipes[0]);
                std::cout<<"input.txt"<<std::endl;
                dup2(stdOut,0);
                std::cout<<"parent done\n";
                wait(NULL);
        }

        std::cout<<"after"<<std::endl;
        return 0;
 }

回答1:

There are several things that should be fixed in your program:

  1. Use STDIN_FILENO and STDOUT_FILENO instead of 0 and 1. This values may change on different platforms and you have also made a mistake which could probably be avoided if you've used names instead of value, e.g. dup2(stdOut,0); duplicated stdin and you need to duplicate stdout here.
  2. You should close write end of the pipe in both child and parent.
  3. By making wc read from stdin, you are then passing "input.txt" string to it - it will return stats for that string, not for the file. You could either fix it be opening a file descriptor for that file or using exec* with cat.
  4. None of your calls the functions like pipe() or execlp() checks for failure. You should do it like that:

    if (pipe(pipes) == -1) {
        perror("pipe");
        exit(1);
    }
    
  5. You don't need stdIn variable.

You will find fixed code below (it does not implement what I've described in the (5) though):

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

int main(int argc, char *argv[]) {
    int pipes[2], pid, stdOut;

    stdOut = dup(STDOUT_FILENO);

    pipe(pipes);

    pid = fork();

    if (pid == 0) {
        dup2(pipes[0], STDIN_FILENO);
        /* You need to close write end of the pipe here */
        close(pipes[1]);
        execlp("wc", "wc", NULL);
    } else {
        std::cout << "Parent setup" << std::endl;
        dup2(pipes[1], STDOUT_FILENO);
        /* You need to close write end of the pipe here as well */
        close(pipes[1]); 
        /* This will only send the string "input.txt" through the pipe, to the
         * wc command */
        std::cout << "input.txt" << std::endl;
        dup2(stdOut, STDOUT_FILENO);
        std::cout << "Parent done" << std::endl;
        wait(NULL);
    }

    std::cout << "Program finished" << std::endl;
    return 0;
}

EDIT: As suggested in the comment to the other answer, you could simple use xargs wc to read stdint as file argument:

execlp("xargs", "xargs","wc",NULL);


回答2:

You have the pipe backwards, you have connected the write end of the pipe to the standard input of wc. You will need to close the write end of the pipe in both processes before wc will detect an end of file condition and terminate normally.

You also incorrectly restore the original standard output to the standard input of the parent.

Furthermore wc will by default not interpret standard input as a list filenames and will therefore not read input.txt.



标签: c++ pipe fork