Given the following program in Python:
import sys
print("input")
while True:
pass
and the equivalent in C:
#include <stdio.h>
int main() {
printf("input\n");
while(1);
return 0;
}
When I pipe the program with cat
, the output is by default buffered (instead of line-buffered). As a consequence, I have no output:
% python3 ./a.py | cat
(empty)
% ./a.out | cat
(empty)
I can use stdbuf
to switch back to line-buffering:
% stdbuf -oL ./a.out | cat
input
But that doesn't work with Python:
% stdbuf -oL python3 a.py | cat
(empty)
Any idea why? I'm aware of the existence of python3 -u
but I want line-buffering, not "no buffering" and I want this command line to work for other languages as well. The command unbuffer
seems to work too, but I'd like to understand why stdbuf
doesn't work here.
By default, Python's print()
function directs its output to sys.stdout
, whose documentation specifies this:
When interactive,
stdout
andstderr
streams are line-buffered. Otherwise, they are block-buffered like regular text files. You can override this value with the-u
command-line option.
Note well that those docs do not leave room for general environmental influences, such as that of the stdbuf
command, on the buffering mode of sys.stdout
: it is unbuffered if the -u
option was used (or, equivalently, if environment variable PYTHONUNBUFFERED
was set), otherwise line-buffered if interactive and block-buffered if non-interactive.
Programs can control their own buffering, as the docs of stdbuf
acknowledge:
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then that will override corresponding settings changed by 'stdbuf'. Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, and are thus unaffected by 'stdbuf' settings.
Since Python explicitly specifies buffering details, it is reasonable to expect that it in fact does affirmatively manage its buffering, thus mooting any effect of stdbuf
.
It looks like python
decides whether to use buffering or not based on isatty
.
I used this script (from Trick an application into thinking its stdout is a terminal, not a pipe):
faketty() {
script -qfc "$(printf "%q " "$@")" /dev/null
}
And it works:
% faketty python3 a.py | cat
input
(And it's line-buffered)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With