我是编程新手,我正在尝试为Linux编写一个c++程序,该程序将创建一个子进程,该子进程将执行一个外部程序。这个程序的输出应该重定向到主程序,并保存到一个字符串变量中,保留所有的空格和新行。我不知道输出将包含多少行/字符
这是基本思想:
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
int pipeDescriptors[2];
pipe(pipeDescriptors);
pid_t pid = fork();
if (pid == -1)
{
std::cerr << __LINE__ << ": fork() failed!n" <<
std::strerror(errno) << 'n';
return 1;
}
else if (!pid)
{
// Child process
close(pipeDescriptors[0]); // Not gonna read from here
if (dup2(pipeDescriptors[1], STDOUT_FILENO) == -1) // Redirect output to the pipe
{
std::cerr << __LINE__ << ": dup2() failed!n" <<
std::strerror(errno) << 'n';
return 1;
}
close(pipeDescriptors[1]); // Not needed anymore
execlp("someExternalProgram", "someExternalProgram", NULL);
}
else
{
// Parent process
close(pipeDescriptors[1]); // Not gonna write here
pid_t stdIn = dup(STDIN_FILENO); // Save the standard input for further usage
if (dup2(pipeDescriptors[0], STDIN_FILENO) == -1) // Redirect input to the pipe
{
std::cerr << __LINE__ << ": dup2() failed!n" <<
std::strerror(errno) << 'n';
return 1;
}
close(pipeDescriptors[0]); // Not needed anymore
int childExitCode;
wait(&childExitCode);
if (childExitCode == 0)
{
std::string childOutput;
char c;
while (std::cin.read(&c, sizeof(c)))
{
childOutput += c;
}
// Do something with childOutput...
}
if (dup2(stdIn, STDIN_FILENO) == -1) // Restore the standard input
{
std::cerr << __LINE__ << ": dup2() failed!n" <<
std::strerror(errno) << 'n';
return 1;
}
// Some further code goes here...
}
return 0;
}
上述代码的问题在于,当std::cin.get()
函数读取输入流中的最后一个字节时,它实际上并没有"读取";知道";该字节是最后一个,并尝试进一步读取,这导致为std::cin
设置failbit
和eofbit
,因此我以后无法再从标准输入读取。std::cin.clear()
重置这些标志,但stdin
仍然不可用。
如果我可以在不超过流中最后一个字符的情况下获得stdin
内容的精确大小(以字节为单位(,那么我就可以使用std::cin.read()
将这个精确的字节量读取到字符串变量中。但我想没有办法做到这一点
那么我该如何解决这个问题呢?我应该使用一个中间文件将子进程的输出写入其中,然后再从父进程读取吗?
子进程写入管道,但父进程直到子进程终止才读取管道。如果子级写入的内容超过管道缓冲区大小,它会阻止等待父级读取管道,但父级会阻止等待子级终止,从而导致死锁。
为了避免这种情况,父进程必须一直读取管道,直到EOF
,然后才使用wait
获取子进程退出状态。
例如:
// Read entire child output.
std::string child_stdout{std::istreambuf_iterator<char>{std::cin},
std::istreambuf_iterator<char>{}};
// Get the child exit status.
int childExitCode;
if(wait(&childExitCode))
std::abort(); // wait failed.
您可能还想从管道文件描述符中打开一个新的istream
,以避免混淆std::cin
状态。