这个问题源于我尝试实现中的指令
Linux管道作为输入和输出
如何使用管道在两个程序之间发送一个简单的字符串?
http://tldp.org/LDP/lpg/node11.html
我的问题与中的问题大致相同:Linux管道作为输入和输出,但更具体。
本质上,我试图取代:
/directory/program < input.txt > output.txt
使用C++中的管道以避免使用硬盘驱动器。这是我的代码:
//LET THE PLUMBING BEGIN
int fd_p2c[2], fd_pFc[2], bytes_read;
// "p2c" = pipe_to_child, "pFc" = pipe_from_child (see above link)
pid_t childpid;
char readbuffer[80];
string program_name;// <---- includes program name + full path
string gulp_command;// <---- includes my line-by-line stdin for program execution
string receive_output = "";
pipe(fd_p2c);//create pipe-to-child
pipe(fd_pFc);//create pipe-from-child
childpid = fork();//create fork
if (childpid < 0)
{
cout << "Fork failed" << endl;
exit(-1);
}
else if (childpid == 0)
{
dup2(0,fd_p2c[0]);//close stdout & make read end of p2c into stdout
close(fd_p2c[0]);//close read end of p2c
close(fd_p2c[1]);//close write end of p2c
dup2(1,fd_pFc[1]);//close stdin & make read end of pFc into stdin
close(fd_pFc[1]);//close write end of pFc
close(fd_pFc[0]);//close read end of pFc
//Execute the required program
execl(program_name.c_str(),program_name.c_str(),(char *) 0);
exit(0);
}
else
{
close(fd_p2c[0]);//close read end of p2c
close(fd_pFc[1]);//close write end of pFc
//"Loop" - send all data to child on write end of p2c
write(fd_p2c[1], gulp_command.c_str(), (strlen(gulp_command.c_str())));
close(fd_p2c[1]);//close write end of p2c
//Loop - receive all data to child on read end of pFc
while (1)
{
bytes_read = read(fd_pFc[0], readbuffer, sizeof(readbuffer));
if (bytes_read <= 0)//if nothing read from buffer...
break;//...break loop
receive_output += readbuffer;//append data to string
}
close(fd_pFc[0]);//close read end of pFc
}
我绝对相信上面的字符串初始化正确。然而,有两件事对我来说毫无意义:
(1) 我正在执行的程序报告"输入文件为空"。由于我没有用"<"调用程序,因此不应该期望有输入文件。相反,它应该期待键盘输入。此外,它应该读取包含在"gull_command"中的文本
(2) 程序的报告(通过标准输出提供)显示在终端中。这很奇怪,因为这个管道的目的是将stdout传输到我的字符串"receive_output"。但由于它出现在屏幕上,这表明信息没有通过管道正确传递到变量。如果我在If语句的末尾实现以下内容,
cout << receive_output << endl;
我什么也没得到,好像绳子是空的。我很感激你能给我的任何帮助!
编辑:澄清
我的程序当前使用文本文件与另一个程序通信。我的程序编写一个文本文件(例如input.txt),由外部程序读取。然后,该程序生成output.txt,由我的程序读取。所以它是这样的:
my code -> input.txt -> program -> output.txt -> my code
因此,我的代码目前使用
system("program < input.txt > output.txt");
我想用管道代替这个过程。我想将我的输入作为标准输入传递给程序,并让我的代码将该程序的标准输出读取为字符串。
您的主要问题是dup2()
的参数颠倒了。您需要使用:
dup2(fd_p2c[0], 0); // Duplicate read end of pipe to standard input
dup2(fd_pFc[1], 1); // Duplicate write end of pipe to standard output
我被误解了你写的OK,直到我对设置代码进行了错误检查,并从dup2()
调用中得到了意外值,这告诉了我问题所在。当出现问题时,插入您以前跳过的错误检查。
您也没有确保从孩子身上读取的数据无效终止;这个代码确实如此。
工作代码(带诊断),使用cat
作为最简单的"其他命令":
#include <unistd.h>
#include <string>
#include <iostream>
using namespace std;
int main()
{
int fd_p2c[2], fd_c2p[2], bytes_read;
pid_t childpid;
char readbuffer[80];
string program_name = "/bin/cat";
string gulp_command = "this is the command data sent to the child cat (kitten?)";
string receive_output = "";
if (pipe(fd_p2c) != 0 || pipe(fd_c2p) != 0)
{
cerr << "Failed to pipen";
exit(1);
}
childpid = fork();
if (childpid < 0)
{
cout << "Fork failed" << endl;
exit(-1);
}
else if (childpid == 0)
{
if (dup2(fd_p2c[0], 0) != 0 ||
close(fd_p2c[0]) != 0 ||
close(fd_p2c[1]) != 0)
{
cerr << "Child: failed to set up standard inputn";
exit(1);
}
if (dup2(fd_c2p[1], 1) != 1 ||
close(fd_c2p[1]) != 0 ||
close(fd_c2p[0]) != 0)
{
cerr << "Child: failed to set up standard outputn";
exit(1);
}
execl(program_name.c_str(), program_name.c_str(), (char *) 0);
cerr << "Failed to execute " << program_name << endl;
exit(1);
}
else
{
close(fd_p2c[0]);
close(fd_c2p[1]);
cout << "Writing to child: <<" << gulp_command << ">>" << endl;
int nbytes = gulp_command.length();
if (write(fd_p2c[1], gulp_command.c_str(), nbytes) != nbytes)
{
cerr << "Parent: short write to childn";
exit(1);
}
close(fd_p2c[1]);
while (1)
{
bytes_read = read(fd_c2p[0], readbuffer, sizeof(readbuffer)-1);
if (bytes_read <= 0)
break;
readbuffer[bytes_read] = ' ';
receive_output += readbuffer;
}
close(fd_c2p[0]);
cout << "From child: <<" << receive_output << ">>" << endl;
}
return 0;
}
样本输出:
Writing to child: <<this is the command data sent to the child cat (kitten?)>>
From child: <<this is the command data sent to the child cat (kitten?)>>
请注意,您需要小心,以确保您的代码不会陷入僵局。如果你有一个严格同步的协议(因此父级在锁定步骤中写入消息并读取响应),你应该没事,但如果父级试图将一条太大而无法放入管道的消息写入子级,而子级试图将太大而不能放入管道的信息写入父级,那么在等待另一个读取时,每个消息都将被阻止写入。
听起来像是在寻找协处理器。你可以用C/C++对它们进行编程,但由于它们已经在(bash)shell中可用,所以使用shell更容易,对吧?
首先用coproc
内置程序启动外部程序:
coproc external_program
coproc
在后台启动程序,并将与之通信的文件描述符存储在数组外壳变量中。现在你只需要启动你的程序,将其连接到那些文件描述符:
your_program <&${COPROC[0]} >&${COPROC[1]}
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <string.h>
#include <iostream>
using namespace std;
int main() {
int i, status, len;
char str[10];
mknod("pipe", S_IFIFO | S_IRUSR | S_IWUSR, 0); //create named pipe
pid_t pid = fork(); // create new process
/* Process A */
if (pid == 0) {
int myPipe = open("pipe", O_WRONLY); // returns a file descriptor for the pipe
cout << "nThis is process A having PID= " << getpid(); //Get pid of process A
cout << "nEnter the string: ";
cin >> str;
len = strlen(str);
write(myPipe, str, len); //Process A write to the named pipe
cout << "Process A sent " << str;
close(myPipe); //closes the file descriptor fields.
}
/* Process B */
else {
int myPipe = open("pipe", O_RDONLY); //Open the pipe and returns file descriptor
char buffer[21];
int pid_child;
pid_child = wait(&status); //wait until any one child process terminates
int length = read(myPipe, buffer, 20); //reads up to size bytes from pipe with descriptor fields, store results
// in buffer;
cout<< "nnThis is process B having PID= " << getpid();//Get pid of process B
buffer[length] = ' ';
cout << "nProcess B received " << buffer;
i = 0;
//Reverse the string
for (length = length - 1; length >= 0; length--)
str[i++] = buffer[length];
str[i] = ' ';
cout << "nRevers of string is " << str;
close(myPipe);
}
unlink("pipe");
return 0;
}