I tried to run the following command:
cat | lessI know both commands require something from the standard input.
When I run it that's what I expected: all my inputs goes to cat and I will not be able to send commands to less (eventually without being able to quit the program with the command q.).
This is not what happens. Actually, it works properly as long as the number of lines sent in the standard input is less than the number of lines of the screen. When the number of lines sent in the standard input became greather than the number of lines of my consolle, a strange beahviour appeaers. It seems that input is sent sometimes to cat, sometimes to less. For instance, inespectately, the letter q sometims let the program quit.
Why the reason of this behaviour that I (naively) wouldn't expect?
32 Answers
CAT will accept input from stdin (although arguably it more sensibly prefers input from a file or pipe), AND OUTPUTS IT TO STDOUT
This means that the input is simply piped through cat, and then directed on to less. Thus you can see the output in less.
Your confusions are most likely coming in with buffering. CAT is buffering the input and will be batching it (or waiting for end of file) when sending it on to less for formatting.
I hate myself for saying this, but
There’s more than one way to skin a
cat.
By which I mean, there’s standard input, and then there’s standard input. Or, there’s reading from the keyboard (terminal), and then there’s reading from the keyboard. Try
less < afilenameIt works just like
less afilenamewhich means less must have some way of reading from the keyboard
other than reading from the standard input.
Here’s another command that works, even though you might not expect it to:
less afilename < /dev/nullLook at the tty command.
It reports the name of the tty (terminal) connected to the standard input.
It works by calling the ttyname library functionwith an argument of 0 (the file descriptor of the standard input).
less is probably calling ttyname(1)to get the name of the tty (terminal) connected to the standard output.
It then opens that tty for reading, and accepts commands from it.
It does not read commands from the standard input; that’s only for data.
So what we have is two quasi-independent processes (cat and less)independently (concurrently) reading from the keyboard
(i.e., the tty / terminal) on two independent file descriptors.
This is a confusing situation, and is something of a “race condition”.
I find it somewhat analogous to the operation of a pinball machine,
in which there are many lanes, or paths, that the ball can take —
and it always takes one; it can never take more than one. Similarly,
when multiple processes are reading from the same terminal simultaneously,
every line that is typed (if the terminal is in line mode)
or every character that is typed (if the terminal is in character mode)
goes to exactly one process.
And the selection is arbitrary, not unlike the action of the pinball.
It’s not fully “random”, any more than the scheduling of processes is random;
but it’s essentially unpredictable.
So, here’s what happens:
catreads from its standard input, which, by default, is the terminal, and writes to its standard output, which is the pipe toless.- At some point,
lesscallsttyname(1), gets the name of the terminal it is on, and opens that for reading. lessreads a screen-full of data (i.e., 24 lines, or whatever) from its standard input (the pipe) and writes it to the standard output (the terminal).- Then,
lessissues the:prompt, puts the terminal into character mode, and starts reading from the terminal (not from the standard input). - So, we now have two processes (
catandless) reading from the terminal simultaneously, and the pinball phenomenon kicks in — the characters (and/or lines) that you type go to eithercatorless, semi-randomly. If it goes tocat, it gets written to the pipe andlesstreats it as data. If it goes toless, it is interpreted as alesscommand.
This doesn’t really have anything to do with buffering.
2