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;
}
There are several things that should be fixed in your program:
- 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.
- You should close write end of the pipe in both child and parent.
- 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
.
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);
}
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);
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
.