Linux 输入/输出配管
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17508626/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Piping for input/output
提问by Eric Inclan
This question follows from my attempt to implement the instructions in:
这个问题来自我尝试实施以下说明的尝试:
Linux Pipes as Input and Output
How to send a simple string between two programs using pipes?
http://tldp.org/LDP/lpg/node11.html
http://tldp.org/LDP/lpg/node11.html
My question is along the lines of the question in: Linux Pipes as Input and Output, but more specific.
我的问题与以下问题类似:Linux Pipes as Input and Output,但更具体。
Essentially, I am trying to replace:
本质上,我正在尝试替换:
/directory/program < input.txt > output.txt
using pipes in C++ in order to avoid using the hard drive. Here's my code:
在 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
}
I am absolutely sure that the above strings are initialized properly. However, two things happen that don't make sense to me:
我绝对确定上述字符串已正确初始化。但是,发生了两件对我来说没有意义的事情:
(1) The program I am executing reports that the "input file is empty." Since I am not calling the program with "<" it should not be expecting an input file. Instead, it should be expecting keyboard input. Furthermore, it should be reading the text contained in "gulp_command."
(1) 我正在执行的程序报告“输入文件为空”。由于我没有使用“<”调用程序,因此不应期望输入文件。相反,它应该期待键盘输入。此外,它应该阅读“gulp_command”中包含的文本。
(2) The program's report (provided via standard output) appears in the terminal. This is odd because the purpose of this piping is to transfer stdout to my string "receive_output." But since it is appearing on screen, that indicates to me that the information is not being passed correctly through the pipe to the variable. If I implement the following at the end of the if statement,
(2) 程序的报告(通过标准输出提供)出现在终端中。这很奇怪,因为此管道的目的是将 stdout 传输到我的字符串“receive_output”。但由于它出现在屏幕上,这向我表明信息没有通过管道正确传递到变量。如果我在 if 语句的末尾执行以下操作,
cout << receive_output << endl;
I get nothing, as though the string is empty. I appreciate any help you can give me!
我什么也没得到,好像字符串是空的。我很感激你能给我的任何帮助!
EDIT: Clarification
编辑:澄清
My program currently communicates with another program using text files. My program writes a text file (e.g. input.txt), which is read by the external program. That program then produces output.txt, which is read by my program. So it's something like this:
我的程序当前使用文本文件与另一个程序通信。我的程序写入一个文本文件(例如 input.txt),由外部程序读取。然后该程序生成 output.txt,由我的程序读取。所以它是这样的:
my code -> input.txt -> program -> output.txt -> my code
Therefore, my code currently uses,
因此,我的代码目前使用,
system("program < input.txt > output.txt");
I want to replace this process using pipes. I want to pass my input as standard input to the program, and have my code read the standard output from that program into a string.
我想用管道替换这个过程。我想将我的输入作为标准输入传递给程序,并让我的代码将该程序的标准输出读入一个字符串。
采纳答案by Jonathan Leffler
Your primary problem is that you have the arguments to dup2()
reversed. You need to use:
你的主要问题是你有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
I got suckered into misreading what you wrote as OK until I put error checking on the set-up code and got unexpected values from the dup2()
calls, which told me what the trouble was. When something goes wrong, insert the error checks you skimped on before.
在我对设置代码进行错误检查并从dup2()
调用中获得意外值之前,我误读了您所写的内容为 OK ,这告诉我问题出在哪里。当出现问题时,插入您之前跳过的错误检查。
You also did not ensure null termination of the data read from the child; this code does.
您也没有确保从孩子读取的数据的空终止;这段代码可以。
Working code (with diagnostics), using cat
as the simplest possible 'other command':
工作代码(带诊断),使用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 pipe\n";
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 input\n";
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 output\n";
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 child\n";
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] = '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?)>>
';
receive_output += readbuffer;
}
close(fd_c2p[0]);
cout << "From child: <<" << receive_output << ">>" << endl;
}
return 0;
}
Sample output:
示例输出:
coproc external_program
Note that you will need to be careful to ensure you don't get deadlocked with your code. If you have a strictly synchronous protocol (so the parent writes a message and reads a response in lock-step), you should be fine, but if the parent is trying to write a message that's too big to fit in the pipe to the child while the child is trying to write a message that's too big to fit in the pipe back to the parent, then each will be blocked writing while waiting for the other to read.
请注意,您需要小心以确保您的代码不会陷入僵局。如果你有一个严格的同步协议(所以父级写一条消息并在锁步中读取响应),你应该没问题,但是如果父级试图写一个太大的消息,无法放入管道中给子级当孩子试图写一个太大的消息时,管道中无法容纳回父母,那么每个人都将被阻止写入,同时等待另一个人阅读。
回答by Joni
It sounds like you're looking for coprocesses. You can program them in C/C++ but since they are already available in the (bash) shell, easier to use the shell, right?
听起来您正在寻找coprocesses。您可以在 C/C++ 中对它们进行编程,但由于它们已经在 (bash) shell 中可用,因此更易于使用 shell,对吗?
First start the external program with the coproc
builtin:
首先使用coproc
内置程序启动外部程序:
your_program <&${COPROC[0]} >&${COPROC[1]}
The coproc
starts the program in the background and stores the file descriptors to communicate with it in an array shell variable. Now you just need to start your program connecting it to those file descriptors:
的coproc
在后台启动该程序并存储该文件描述符以阵列壳变量与它通信。现在你只需要启动你的程序将它连接到这些文件描述符:
#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<< "\n\nThis 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;
}