【问题标题】:C++ - Duplicating stdout/stderr to file while keeping console outputsC++ - 将标准输出/标准错误复制到文件,同时保持控制台输出
【发布时间】:2017-06-09 11:54:36
【问题描述】:

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

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

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

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

有没有更好的方法来解决这个问题?

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

我正在调用我无法修改且托管在我的进程中的(第 3 方)代码。所以我不能使用自定义流(被调用的代码已经写入标准错误/标准输出)。

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
}

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

【问题讨论】:

  • 在您链接到的第一个问题中,请注意最佳答案只是一个执行两个输出的简单函数?一个到文件,一个到std::cout?不能用类似的方法解决吗?通过创建一个您调用的函数,然后写入文件和stdout?
  • 为什么自定义流如此糟糕?现有的解决方案有什么问题?你只是不喜欢它们,因为它还不是ostream 的一部分?
  • 另外,您能否详细说明您需要写信给stdoutstderr 吗?您是否正在调用一些您无法修改的 C 函数?您试图解决的实际原始问题是什么?请read about the XY problem 思考它与您的问题有何关系。
  • 不,没有标准的类可以做你想做的事。

标签: c++ redirect pipe stdout


【解决方案1】:

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

伪代码:

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 等。

【讨论】:

  • 迄今为止最好的答案!您可能知道如何使用 Windows 命名管道进行这项工作吗? (至于我的问题中的编辑)
  • 抱歉,我的Windows知识非常有限
【解决方案2】:

您可以重定向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
}

正如您在此处看到的,cout 使用的 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

【讨论】:

  • 感谢您的代码。但是,我无法更改被调用的代码,因此我需要复制 stdout/stderr - 正是那些
  • @SimonKraemer,我在 vs2015 中试过你的代码,它只写入文件,而不是控制台。
  • @Robin 你是指实际的控制台还是 Visual Studio 中的调试窗口?这是2个不同的东西。请从命令行运行程序,而不是在 VS 中进行检查。
  • @SimonKraemer,我正在使用它 AllocConsole,我的错,它现在正在工作。
猜你喜欢
  • 2011-03-09
  • 2012-07-15
  • 1970-01-01
  • 1970-01-01
  • 2019-09-03
  • 1970-01-01
  • 1970-01-01
  • 2016-08-07
  • 1970-01-01
相关资源
最近更新 更多