My output gets corrupted when I try to pipe some output to another command in the shell. This doesn't happen when I execute the executable alone, or redirect its output to a program that doesn't care.
My executable
A simple program to print the size of the terminal window in Linux:
/* winsize.cpp to print terminal window size */
#include <iostream>
#include <sys/ioctl.h>
#include <unistd.h>
int main() {
struct winsize w;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
int rows = w.ws_row;
int cols = w.ws_col;
printf("%d lines, %d columns\n %dx%d\n", rows, cols, cols, rows);
printf("%d\n%d\n", cols, rows);
return 0;
}
I know I used printf()
. I'm getting there.
I compile it, and then run it with its relative path ./winsize
or with an alias to its absolute path winsize
.
$ winsize
56 lines, 213 columns
213x56
213
56
$
This is the correct output.
Piping output
Now, I want to do something with the output, but the expected output never comes when I use another program with it.
Line isolation
$ winsize | sed '3q;d'
# expected output:
# 213
29058
$
An incorrect number is given. This number is random each time but appears to be restricted to integers in [0, 65535].
Pipe to self
$ winsize | winsize
# expected output is simply the correct output (because stdin is not read from)
56 lines, 213 columns
213x56
213
56
$
This time the output is correct because this executable is not reading from stdin.
Piping to an a priori program that does read from stdin
Perhaps it was a problem with Linux programs. I wrote my own to test this. The program simply reads each byte from stdin and write the ones read so far to stdout upon reading a newline, including the newline. The program will halt when EOF is read from stdin.
Testing the compiled program with piping from a Linux program:
$ echo "hello" | ./test_echo
# expected output:
# hello
hello
$
This new program works correctly with an arbitrary line print.
Now, giving this program my executable:
$ winsize | ./test_echo
# expected output is simply the correct output
28864 lines, 24898 columns
24898x28864
24898
28864
$
It is incorrect, again, and with 28864 again.
Process substitution into an a priori program that does read from stdin
The same new program is used.
$ test_echo < <(winsize)
# expected output is simply the correct output
28864 lines, 7778 columns
7778x28864
7778
28864
$
Even with this method, an incorrect answer is produced.
Piping to an a priori program that does not read from stdin
$ winsize | ./isprime 15965253440080127741
# expected output:
# 1
1
$
This newer program's output is correct, because there was no read from stdin, I assume.
Problem
The correct information appears if and only if I am running the executable with no other commands/executables that read the output of the program (whether by ./winsize
or winsize
), discounting coincidence. Neither of these will ever produce output that is incorrect, and running the executable with something else will always cause incorrect output.
All other programs pipe correctly. I have no issues with things like ls / | tac
.
28864 for the number of lines seems to be a constant. The number of columns is random and fluctuates every time output is generated.
But, if I change <iostream>
to <stdio.h>
and compile it as C++ with g++, I get the following.
$ winsize | cat
18425 lines, 48255 columns
48255x18425
48255
18425
$
Now both are randomly fluctuating.
Now, if I compile it as C with gcc, I get the following.
$ winsize | cat
4096 lines, 0 columns
0x4096
0
4096
$
It's always 4096 lines, 0 columns. Every time. This is constant.
And then, if I replace the printf()
lines with std::cout
instead, while also restoring the directive to include <iostream>
:
std::cout << rows << " lines, " << cols << " columns\n";
std::cout << " " << cols << "x" << rows << "\n";
std::cout << cols << "\n" << rows << "\n";
And compile it as C++ with g++, I get the following.
$ winsize | cat
28296 lines, 4546 columns
4546x28296
4546
28296
$
The 28296 is constant, and the number of columns reported is random.
I have no idea what's going on.
The first and second lines of the output are for quick human parsing, but the third and fourth lines are helpful for passing the information along to other programs. Unfortunately, the program thinks it's Superman and would rather work alone.