C ++ - 将stdout / stderr复制到文件,同时保持控制台输出

时间:2017-01-24 15:43:49

标签: c++ redirect pipe stdout

这里已经提出了非常相似的问题: Writing to both terminal and file c++

但没有一个好的答案。所有答案都建议使用自定义流或复制std :: cout。但是我需要stdout / stderr的行为。

通缉行为:对于每次写入stdout / stderr,我希望它出现在控制台上,并重定向到文件。

我正在考虑将stdout重定向到管道并从那里写入文件和控制台 - 扩展这个答案https://stackoverflow.com/a/956269/2308106

有没有更好的方法呢?

EDIT1: 为什么stdout / stderr而不是自定义流?

我正在调用(第三方)代码,我无法修改并且托管在我的流程中。所以我不能使用自定义流(被调用的代码已经写入stderr / stdout)。

EDIT2:

根据JvO的建议,我尝试了我的实现(windows):

HANDLE hPipe = ::CreateNamedPipe(TEXT("\\\\.\\pipe\\StdErrPipe"),
    PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
    PIPE_TYPE_BYTE,
    //single instance
    1,
    //default buffer sizes
    0,
    0,
    0,
    NULL);

if (hPipe == INVALID_HANDLE_VALUE)
{
    //Error out
}

bool fConnected = ConnectNamedPipe(hPipe, NULL) ?
    TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

if (!fConnected)
{
    //Error out
}

int fd = _open_osfhandle(reinterpret_cast<intptr_t>(hPipe), _O_APPEND | /*_O_WTEXT*/_O_TEXT);

if (dup2(fd, 2) == -1)
{
    //Error out
}

但是仍然存在一些问题 - 因为从管道的另一端我只收到一些红色(我首先尝试直接发送一些东西 - 效果很好;但是一旦stderr被重定向并且我写了它;管道收到一遍又一遍地使用相同的不可推荐的字符)

2 个答案:

答案 0 :(得分:1)

您可以重定向R。一个(不完整的)示例可能如下所示:

cout

正如你在这里看到的那样,#include <fstream> #include <iostream> template<class CharT, class Traits = std::char_traits<CharT> > struct teestream : std::basic_streambuf<CharT, Traits> { private: std::basic_streambuf<CharT, Traits>* m_rdbuf1; std::basic_streambuf<CharT, Traits>* m_rdbuf2; public: teestream(std::basic_streambuf<CharT, Traits>* rdbuf1, std::basic_streambuf<CharT, Traits>* rdbuf2) :m_rdbuf1(rdbuf1) ,m_rdbuf2(rdbuf2) {} ~teestream() { m_rdbuf1->pubsync(); m_rdbuf2->pubsync(); } protected: int_type overflow(int_type ch = Traits::eof()) override { int_type result = m_rdbuf1->sputc(ch); if (result != Traits::eof()) { result = m_rdbuf2->sputc(ch); } return result; } virtual int sync() override { int result = m_rdbuf1->pubsync(); if (result == 0) { result = m_rdbuf2->pubsync(); } return result; } }; typedef teestream<char, std::char_traits<char>> basic_teestream; int main() { std::ofstream fout("out.txt"); std::streambuf *foutbuf = fout.rdbuf(); //Get streambuf for output stream std::streambuf *coutbuf = std::cout.rdbuf(); //Get streambuf for cout std::streambuf *teebuf = new basic_teestream(coutbuf, foutbuf); //create new teebuf std::cout.rdbuf(teebuf);//Redirect cout std::cout << "hello" << std::endl; std::cout.rdbuf(coutbuf); //Restore cout delete teebuf; //Destroy teebuf } 使用的streambuf被一个控制streambuf本身以及一个ofstream流的streambuf取代。

代码很可能存在许多缺陷并且不完整但你应该明白这一点。

来源:
https://stackoverflow.com/a/10151286/4181011
How can I compose output streams, so output goes multiple places at once?
http://en.cppreference.com/w/cpp/io/basic_streambuf/pubsync
Implementing std::basic_streambuf subclass for manipulating input

答案 1 :(得分:1)

你可以劫持&#39; stdout和stderr通过替换指针; stdout和stderr只不过是FILE *。我建议你首先打开一个管道对,然后使用fdopen()创建一个新的FILE *,它与管道的发送端相关联,然后将stdout指向你的新文件。使用管道的接收端提取写入“旧”的内容。标准输出。

伪代码:

int fd[2];
FILE *old_stdout, *new_stdout;

pipe(fd);
new_stdout = fdopen(fd[1], "w");
old_stdout = stdout;
stdout = new_stdout;

现在,您从fd [0]读取的所有内容都可以写入文件old_stdout等。