How do programs like less receive keyboard input as well as from a pipe?
(Inspired by: https://discuss.python.org/t/_/107345)
It was brought to my attention that it actually works to do something like:
$ spam=$(printf '%1000s')
$ spam=${spam// /abcdefg}
$ echo $spam | less
(There are other TUI programs I use where this doesn't work; whatever is received from the pipe is interpreted as if it were typed by the user, and then the program hangs if it hasn't yet received a "quit" command in the input.)
We have multiple pages of text fed to the standard input of less, which is then able to show the first page and wait for the usual sort of commands from the user.
But how? I thought that the program is reading those commands from standard input, but that was already redirected via a pipe. In other programs I've created myself, once standard input was attached somewhere, it would be impossible to "feed" it from somewhere else (e.g. if I have foo | bar already running, I can find no way for baz to write to bar's input).
And even if the key presses were being translated into input fed to less's stdin, how would it be able to distinguish which input comes from the keyboard vs. the pipe? (It clearly can't do this by inspecting the actual byte values, since less accepts commands that are ordinary text, such as q, rather than purely relying on things that would start with a control character.)
1 answer
The following users marked this post as Works for me:
| User | Comment | Date |
|---|---|---|
| Karl Knechtel | (no comment) | May 14, 2026 at 17:46 |
As you've observed, programs like less don't rely on STDIN for interactive input. Instead, they look for their controlling terminal and open a new file descriptor reading from that. On most systems, that's achieved by opening /dev/tty for reading. Much as with STDIN/STDOUT/STDERR, a Unix process usually inherits its controlling terminal from its parent, in this case the shell. See also tty(4) and credentials(7).
Another program that does this is sudo, which is why you can type things like:
echo foo | sudo tee /root/bar
and it'll work even if sudo wants a password. What's more, it uses the controlling terminal for printing the password prompt too, so you could even do:
cut -d: -f1 /etc/passwd | sudo xargs getent shadow | less
and it still works.

0 comment threads