1

Can someone please explain why the output I get from running a simple C++ program in a Windows console is a bit garbled when it is piped to the more command.

Here's the code:

#include <iostream>
int main()
{
    for(int i=101; i<=120; ++i){
        std::cerr << "  cerr " << i << std::endl;
        if(i%5==0){
            std::cout << "  cout " << i << std::endl;
        }
    }
}

Both of these commands produce garbled output after the first few lines. The output differs on each run. Examples are given below:

mycode.exe | more

...
  cerr 117
  cerr 118
                                                                                                                                                                                                er cout 105
1 cout 110r
1  cout 1159

  cerr 120
  cout 120

mycode.exe > nul | more

...
  cerr 114
  cerr 115
6 cerr 11
                                                                                                                                                                                                 cerr 117
  cerr 118
...

Note that some of the lines have a lot of spaces as you can see from the horizontal scroll bars.

On Linux, ./mycode | more also gives garbled results but ./mycode > /dev/null | more does not.

The output without more is fine for both Windows and Linux:

  cerr 101
  cerr 102
  cerr 103
  cerr 104
  cerr 105
  cout 105
  cerr 106
  cerr 107
  cerr 108
  cerr 109
  cerr 110
  cout 110
  cerr 111
  cerr 112
  cerr 113
  cerr 114
  cerr 115
  cout 115
  cerr 116
  cerr 117
  cerr 118
  cerr 119
  cerr 120
  cout 120

I am using the Visual Studio C++ 2017 Community Edition version 15.7.4 on Windows 10. The program is compiled in 64 bit mode as a Release version. I think this is easy to reproduce and get the same sort of results whether compiled using the IDE or something like the following: CL /nologo /D WIN32 /D_WIN32_WINNT=0x502 /EHsc mycode.cpp - those flags are probably not all needed since it's such a simple program but happen to be included in a bat file I typically use for testing.

As an aside, an example of Linux output using ./mycode | more is not quite a as bizarre, but it's appears a little odd all the same:

...
  cerr 109
  cerr 110
  cerr   cout 110
111
  cerr 112
  cerr 113
  cerr 114
  cerr 115
  cerr   cout 115
116
  cerr 117
...
fundamental
  • 117
  • 1
  • 6
  • Does this answer your question? [Why cerr output faster than cout?](https://stackoverflow.com/questions/34663495/why-cerr-output-faster-than-cout) – Amadeus Dec 02 '19 at 10:49
  • @Amadeus I don’t see what this has to do with OP’s question. – Konrad Rudolph Dec 02 '19 at 11:01
  • I cannot reproduce the output you are seeing, and a conforming C++ compiler **must not** produce a binary that produces such output from your code, which has well-defined behaviour. Can you please specify your exact compiler, and how you invoke it? Either you didn’t post the code you’re actually compiling, or your compiler is seriously broken. – Konrad Rudolph Dec 02 '19 at 11:03
  • What is the output of the executable without piping it to more? Because it is very valid that the output with piping it to more is garbled. The pipe only redirects stdout to more, the stderr is only printed to the console and that will interfere with the output from more. So the behavior on linux is totally expected. That windows is a bit weird on the second output also not necessarily wrong, since maybe more just hits a carriage return without newline if it gets empty input or so (dunno, I have never worked with a shell under win). – n314159 Dec 02 '19 at 11:13
  • I'm not familiar with the Windows console but are you sure the command lines shown *are* redirecting stderr? With linux/bash I'd expect to see something like `mycode 2>&1 | more` – G.M. Dec 02 '19 at 11:41

1 Answers1

0

Expanding from my comment. There is nothing really unexpected about your code. If you pipe the output of a program to something different, per default only stdout is passed to this program. The stderr is still jus printed on the console . So without redirecting to null, the stderr from your program and the output from more will print to the the console simultaneously and not snychronized since the more command is forked (? not exactly sure about that but near enough). Since there are delays in piping and the handling of input in more, this will mess with the output order.

With redirecting to null, you only redirect stdout to null and your more gets empty input and stderr prints to the console, so there should be no interference, which you see on linux. On windows it seems that more outputs a carriage return or something like that on empty input, I am not sure, but this is also not really unecpected.

What you always want to so if you are piping and don't want your output to be messed up is one of the following:

# Direct stderr to null, only sends stdout to pipe
prog 2>/dev/null | piper
# Direct stderr to stdout, sends both to pipe
prog 2>&1 | piper
# Direct stdout to /dev/null and stderr to stdout, sends just stdout to pipe
prog 2>&1 1>/dev/null | piper

The last one seems a bit weird since ine would assume that it redirects stdout also to null, but it will redirect it there were stdout was being directed when this redirection was declared.

You probably want the second variant.

Note: No idea how this works on windows, I use arch btw ;-)

n314159
  • 4,990
  • 1
  • 5
  • 20
  • I would not expect to ever observe this output. It *may* theoretically be possible but where for instance would the solitary “r” in OP’s output come from? Do you think the “cer” got swallowed? That still wouldn’t explain the extraneous “1”s. No, something is seriously broken here. – Konrad Rudolph Dec 02 '19 at 12:07
  • @KonradRudolph I wouldn't be to sure about that, that's why I am interested in the output from the program without more. I could imagine a carriage return from more setting us to the begin of the line already written to by stderr and then overwriting whats already there. – n314159 Dec 02 '19 at 12:25