C Redirect Stdout to File

为了在C代码里实现类似Shell脚本的功能: ./exe 2>&1 tee log.txt

1. Use freopen

// output console and file simutainiously
class RedirectStdIO {
public:
    RedirectStdIO(string str, bool needStd = true): path(str), need_std(needStd) {
        std_out = dup(fileno(stdout));  // 保存下来,方便后续找回std_out
        std_err = dup(fileno(stderr));
        if(!direct2file(path))
            std::cerr << "Can not redirect output to file " << path << std::endl;
    }
    bool direct2file(string str){
        if(str.empty()) return false;
        fp = freopen(str.c_str(), "w", stdout);
        dup2(fileno(stdout), fileno(stderr));  // 重定向
        return (fp!=nullptr);
    }
    void back2stdout() {
        fflush(fp);
        dup2(std_out, fileno(stdout));  // cout重回screen
        dup2(std_err, fileno(stderr));
        close(std_out);
        close(std_err);
        // cout lines in file path to stdout
        string line;
        ifstream myfile(path);
        if (myfile.is_open()) {
            while ( getline (myfile,line) ) {
                cout << line << '\n';
            }
            myfile.close();
        }
        cout.flush();
        need_std = false;
    }
    ~RedirectStdIO(){
        if(need_std) back2stdout();
        if(fp) fflush(fp);
        if(fp) fclose(fp);
    }
private:
    FILE *fp = nullptr;
    int std_out, std_err;
    bool need_std;
    string path;
};

2. Use c11 streambuf

referenc

reference2

#include <iostream>
#include <streambuf>
#include <fstream>

//------------------------------------------------------------------------------

template <class charT, class traits = std::char_traits<charT> >
class basic_teebuf : public std::basic_streambuf<charT, traits>
{
public:
    typedef charT char_type;
    typedef typename traits::int_type int_type;
    typedef typename traits::pos_type pos_type;
    typedef typename traits::off_type off_type;
    typedef traits traits_type;
    typedef std::basic_streambuf<charT, traits> streambuf_type;

private:
    streambuf_type *m_sbuf1;
    streambuf_type *m_sbuf2;
    char_type *m_buffer;
    enum {BUFFER_SIZE = 4096 / sizeof(char_type)};

public:
    basic_teebuf(streambuf_type *sbuf1, streambuf_type *sbuf2)
        : m_sbuf1(sbuf1), m_sbuf2(sbuf2), m_buffer(new char_type[BUFFER_SIZE])
    {
        setp(m_buffer, m_buffer + BUFFER_SIZE);
    }//constructor
    ~basic_teebuf()
    {
        this->pubsync();
        delete[] m_buffer;
    }//destructor
protected:
    virtual int_type overflow(int_type c = traits_type::eof())
    {
        // empty our buffer into m_sbuf1 and m_sbuf2
        std::streamsize n = static_cast<std::streamsize>(this->pptr() - this->pbase());
        std::streamsize size1 = m_sbuf1->sputn(this->pbase(), n);
        std::streamsize size2 = m_sbuf2->sputn(this->pbase(), n);
        if (size1 != n || size2 != n)
            return traits_type::eof();
        // reset our buffer
        setp(m_buffer, m_buffer + BUFFER_SIZE);
        // write the passed character if necessary
        if (!traits_type::eq_int_type(c, traits_type::eof()))
        {
            traits_type::assign(*this->pptr(), traits_type::to_char_type(c));
            this->pbump(1);
        }//if
        return traits_type::not_eof(c);
    }//overflow

    virtual int sync()
    {
        // flush our buffer into m_sbuf1 and m_sbuf2
        int_type c = this->overflow(traits_type::eof());
        // checking return for eof.
        if (traits_type::eq_int_type(c, traits_type::eof()))
            return -1;
        // flush m_sbuf1 and m_sbuf2
        if (m_sbuf1->pubsync() == -1 || m_sbuf2->pubsync() == -1)
            return -1;
        return 0;
    }//sync
};//basic_teebuf
typedef basic_teebuf<char>    teebuf;
typedef basic_teebuf<wchar_t> wteebuf;
//------------------------------------------------------------------------------

template <class charT, class traits = std::char_traits<charT> >
struct scoped_basic_streambuf_assignment
{
    typedef std::basic_ios<charT, traits> stream_type;
    typedef std::basic_streambuf<charT, traits> streambuf_type;
    stream_type &m_s;
    streambuf_type *m_orig_sb;
    scoped_basic_streambuf_assignment(stream_type &s, streambuf_type *new_sb) 
        : m_s(s)
    {
        m_orig_sb = m_s.rdbuf(new_sb);
    }//constructor
    ~scoped_basic_streambuf_assignment()
    {
        m_s.rdbuf(m_orig_sb);
    }//destructor
};//scoped_streambuf_assignment
typedef scoped_basic_streambuf_assignment<char>    scoped_streambuf_assignment;
typedef scoped_basic_streambuf_assignment<wchar_t> scoped_wstreambuf_assignment;
//------------------------------------------------------------------------------
int main()
{
    std::ofstream file("data.txt");
    std::ofstream file2("data2.txt");
    // tbuf will write to both file and cout
    teebuf tbuf(file.rdbuf(), std::cout.rdbuf());
    // tbuf2 will write to both tbuf and file2
    teebuf tbuf2(file2.rdbuf(), &tbuf);
    // replace cout's streambuf with tbuf2
    scoped_streambuf_assignment ssa(std::cout, &tbuf2);
    std::cout << "Hello World" << std::endl;
    return 0;
}//main
Loading Disqus comments...
Table of Contents