There's a general buffering rule followed by the C standard I/O library (stdio
) that most unix programs use. If output is going to a terminal, it is flushed at the end of each line; otherwise it is flushed only when the buffer (8K on my Linux/amd64 system; could be different on yours) is full.
If all your utilities were following the general rule, you would see output delayed in all of your examples (cat|sed
, cat|tr
, and cat|tr|sed
). But there's an exception: GNU cat
never buffers its output. It either doesn't use stdio
or it changes the default stdio
buffering policy.
I can be fairly sure you're using GNU cat
and not some other unix cat
because the others wouldn't behave this way. Traditional unix cat
has a -u
option to request unbuffered output. GNU cat
ignores the -u
option because its output is always unbuffered.
So whenever you have a pipe with a cat
on the left, in the GNU system, the passage of data through the pipe will not be delayed. The cat
isn't even going line by line - your terminal is doing that. While you're typing input for cat, your terminal is in "canonical" mode - line-based, with editing keys like backspace and ctrl-U offering you the chance to edit the line you have typed before sending it with Enter.
In the cat|tr|sed
example, tr
is still receiving data from cat
as soon as you press Enter, but tr
is following the stdio
default policy: its output is going to a pipe, so it doesn't flush after each line. It writes to the second pipe when the buffer is full, or when an EOF is received, whichever comes first.
sed
is also following the stdio
default policy, but its output is going to a terminal so it will write each line as soon as it has finished with it. This has an effect on how much you must type before something shows up on the other end of the pipeline - if sed
was block-buffering its output, you'd have to type twice as much (to fill tr
's output buffer and sed
's output buffer).
GNU sed
has -u
option so if you reversed the order and used cat|sed -u|tr
you would see the output appear instantly again. (The sed -u
option might be available elsewhere but I don't think it's an ancient unix tradition like cat -u
) As far as I can tell there's no equivalent option for tr
.
There is a utility called stdbuf
which lets you alter the buffering mode of any command that uses the stdio
defaults. It's a bit fragile since it uses LD_PRELOAD
to accomplish something the C library wasn't designed to support, but in this case it seems to work:
cat | stdbuf -o 0 tr '[:lower:]' '[:upper:]' | sed 'p'
cat
buffering until stdin closes. – goldilocks Jan 31 '15 at 21:46tr
andsed
do process lines fromcat
before stdin closes – xealits Jan 31 '15 at 21:49