lo tolmencre
lo tolmencre

Reputation: 3934

piping cout interferes with cerr

I wrote a header for a progress bar. It contains the following function:

/**
 * @brief advances the progress bar every time an interval of x
 *        function calls has been completed
 * @param current the current iteration
 * @param o the stream to write to
 */
inline void run(step current, sost& o=std::cerr)
{
    if(current%interval == 0 && !(current > max))
    {
        // do away with the cursor
        #ifdef UNIXLIKE
        printf("\e[?25l");
        #endif

        // fill line with background symbol
        for (int i = 0; i < PSD+lbracketSize; ++i) o << " ";
        for (unsigned i = 0; i < pres; ++i) o << pre;
        o << rbracket << "\r";
        o.flush();

        // calculate percentage and make a string of it
        float prct = ((float)current/max)*100;
        sstr strprct = helper::to_string((int)prct);
        // get percantge to length of three
        if (helper::utf8_size(strprct) < 2) strprct = " "+strprct;
        if (helper::utf8_size(strprct) < 3) strprct = " "+strprct;

        // print percentage and bar
        o << "[" << strprct << "% ] " << lbracket;
        for (auto i = pbar.begin(); i != pbar.end(); ++i) o << *i;

        o << "\r";
        o.flush();

        pbar.push_back(bar);
    }
    if(current>=max-1)
    {
        cancel();
    }
}

It works fine. As you can see I send the progress bar to stderr. The idea behind that was, that I can pipe the output of my program, which goes to stdout, without catching the progress bar on stderr. But in practice it does not quite work. While the progress bar does not get catched by the pipe as intended, the \rcommand seems to not take effect, as the bar is written with ever new lines, rather than stay on the same line, as it does, when I don't pipe stdout. Why is that? Doesn't make any sense to me.

so, normally the bar looks like this:

[ 37% ] ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

When I pipe stdout to a file, I get this all over my terminal for countless lines:

        ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

Upvotes: 3

Views: 294

Answers (1)

Andrey Nasonov
Andrey Nasonov

Reputation: 2629

Please, check if the following code does not work fine for you if stdout is redirected:

#include <iostream>

int main(int argc, char ** argv)
{
    std::cerr << "Hello,\rWorld!\n";
    return 0;
}

I have some suggestions for your code:

  1. The line printf("\e[?25l"); should be changed to o << "\e[?25l". You should not mix stdio.h with iostream and you should output the control sequence to stderr.

  2. stderr can be redirected too. You need to check if the stream is terminal and detect the capabilities of the terminal. The implementation is system specific. One of the solutions can be found here. Also this link may be helpful.

  3. You can forcibly output to the terminal by getting the name of the terminal using tty command and writing to it.

Upvotes: 3

Related Questions