将stdout / stderr重定向到unix c ++下的文件 - 再次

时间:2013-08-06 17:00:42

标签: c++ c io file-descriptor io-redirection

我想做什么

将stdout和stderr重定向到c ++中的一个或多个文件

为什么我需要它

我正在使用外部预编译的第三方库,它会产生大量的输出,我想将其重定向到日志文件以保持控制台清洁。

条件

兼容性不是问题,代码只能在Unix系统上运行。重定向不仅应该影响c ++风格的打印(std :: cout<<“hello world”<< std :: endl),还应该影响c风格的打印(printf(“hello world \ n”))

到目前为止我尝试了什么

我一直在浏览stackoverflow半天,为有类似问题的人阅读多个答案。借助这些答案,我已经能够将以下代码组合在一起:


#include <stdio.h>
#include <iostream>
#include <fcntl.h>
#include "unistd.h"

const int stdoutfd(dup(fileno(stdout)));

int redirect_stdout(const char* fname){
  fflush(stdout);
  int newstdout = open(fname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP |     S_IROTH);
  dup2(newstdout, fileno(stdout));
  close(newstdout);
}

int restore_stdout(){
  fflush(stdout);
  dup2(stdoutfd, fileno(stdout));
  close(stdoutfd);
  return stdoutfd;
}

int main(){
  redirect_stdout("/dev/null");
  std::cout << "invisible 1" << std::endl;
  restore_stdout();
  std::cout << "visible 1" << std::endl;
  redirect_stdout("/dev/null");
  std::cout << "invisible 2" << std::endl;
  restore_stdout();
  std::cout << "visible 2" << std::endl;
  return 0;
}

我期望看到的内容:

visible 1
visible 2

我实际看到的是什么

visible 1

也就是说,第一次使用这种机制时,它可以工作 - 但如果再次使用,恢复输出将不起作用。 有人可以向我指出我需要改变什么才能让机制经常无限地工作吗?

6 个答案:

答案 0 :(得分:4)

如果您希望能够重复使用它,请不要关闭stdoutfd中的restore_stdout

答案 1 :(得分:2)

你在寻找这样的东西: -

int main()
{
    // Save original std::cin, std::cout
    std::streambuf *coutbuf = std::cout.rdbuf();
    std::streambuf *cinbuf = std::cin.rdbuf(); 

    std::ofstream out("outfile.txt");
    std::ifstream in("infile.txt");

    //Read from infile.txt using std::cin
    std::cin.rdbuf(in.rdbuf());

    //Write to outfile.txt through std::cout 
    std::cout.rdbuf(out.rdbuf());   

    std::string test;
    std::cin >> test;           //from infile.txt
    std::cout << test << "  "; //to outfile.txt

    //Restore back.
    std::cin.rdbuf(cinbuf);   
    std::cout.rdbuf(coutbuf); 

}

来自我之前的answer

答案 2 :(得分:2)

我会更好地使用freopen()

用法语法:

freopen("RedToFile","r",stdout);
or
freopen("/dev/null","a",stdout);

“stderr”也是如此

答案 3 :(得分:1)

除了afr0ckfreopen()回答之外,我想说在使用freopen()时我们应该小心。通过分配新目标(此处为&#39; output.txt&#39; 文件)重新打开stdoutstdin之类的流后,它始终保留给程序除非它已经明确改变。

freopen("output.txt", "a", stdout);

此处重新打开标准输出流stdout,并使用&#39; output.txt&#39; 文件进行分配。在此之后,只要我们使用printf()或任何其他stdout流,例如 - putchar(),那么每个输出都将转到&#39; output.txt&#39; 。要恢复printf()putchar()的默认行为(即在控制台/终端中打印输出),我们可以使用以下代码行 -

  • 对于gcc,linux发行版如ubuntu - freopen("/dev/tty", "w", stdout);
  • for Mingw C / C ++,windows - freopen("CON", "w", stdout);

请参阅下面的代码示例 -

#include <stdio.h>

int main() {

    printf("No#1. This line goes to terminal/console\n");

    freopen("output.txt", "a", stdout);
    printf("No#2. This line goes to the \"output.txt\" file\n");
    printf("No#3. This line aslo goes to the \"output.txt\" file\n");

    freopen("/dev/tty", "w", stdout); /*for gcc, diffrent linux distro eg. - ubuntu*/
    //freopen("CON", "w", stdout); /*Mingw C++; Windows*/
    printf("No#4. This line again goes to terminal/console\n");        

}

此代码会在当前目录中生成&#39; output.txt&#39; 文件,并且&#39;中将显示No#2和No#3; output.txt&#39; 文件。

由于

答案 4 :(得分:0)

对于C ++ iostream,您可以使用rdbuf的非常量重载 将std::cout设置为std::filebuf。 (最好这样做 RAII类的手段,因为你必须先恢复它 离开main。)对于C FILE*,您可以使用freopen,但是 我认为你无法恢复它。

FWIW:这两种解决方案都只使用标准C ++或C,所以 应该是便携式的。

答案 5 :(得分:0)

我受到@POW和@James Kanze的回答的启发,并整理了一个RAII类,用于将std::cout重定向到文件。旨在演示该原理。

代码:

#include <iostream>
#include <fstream>
#include <string>

// RAII for redirection
class Redirect {
    public:
    
    explicit Redirect(const std::string& filenm):
        _coutbuf{ std::cout.rdbuf() },   // save original rdbuf
        _outf{ filenm }
    {
        // replace cout's rdbuf with the file's rdbuf
        std::cout.rdbuf(_outf.rdbuf());
    }
    
    ~Redirect() {
        // restore cout's rdbuf to the original
        std::cout << std::flush;
        _outf.close();    ///< not really necessary
        std::cout.rdbuf(_coutbuf); 
    }
    
    private:
    
    std::streambuf* _coutbuf;
    std::ofstream _outf;
};

// == MAIN ==

int main(int argc, char* argv[]) {
    std::cout << "This message is printed to the screen" << std::endl;
    {
        // scope for the redirection
        Redirect redirect{ "output.txt" };
        std::cout << "This message goes to the file" << std::endl;
    }
    std::cout << "Printing to the screen again" << std::endl;
}

输出:

此消息被打印到屏幕上

再次打印到屏幕上

文件"output.txt"的内容:

此消息进入文件