less file1 file2 | cat -- why does it work?
less
prints text to stdout. stdout goes
- to a terminal (/dev/tty?) and opens the default buffer viewer
- through a pipe when piping it to another programm using | (
less text | cut -d: -f1
) - to a file when redirecting it with > (
less text > tmp
)
There is a C function called "isatty" which checks if the output is going to a tty (less 4.81, main.c, line 112). If so, it uses the buffer viewer otherwise it behaves like cat
.
In bash you can use test (see man test
)
- -t FD file descriptor FD is opened on a terminal
- -p FILE exists and is a named pipe
Example:
[[ -t 1 ]] && \
echo 'STDOUT is attached to TTY'
[[ -p /dev/stdout ]] && \
echo 'STDOUT is attached to a pipe'
[[ ! -t 1 && ! -p /dev/stdout ]] && \
echo 'STDOUT is attached to a redirection'
less
checks if its stdout
is a terminal, and behaves like cat
when it isn't (copies stdin to stdout until EOF).
This feature lets you write scripts or programs that always send their output (e.g. --help
output) through less
while still allowing easy redirection to a file. It would suck if some_command --fullhelp > help.txt
still waited for space-bar on stdin to page through the text, or something. Some commands (e.g. man
) check that their own output to decide whether to send their output through a pager or not. If you run man ls > ls.txt
, it never invokes your $PAGER
.
less
's cat-like behaviour is handy if you forget to edit it out of a one-liner when adding more stages to a pipeline, too.
less
needs to figure out the terminal dimensions (screen size, to know how many lines to show at once). The ioctl(2)
it uses on stdout
would return ENOTTY on a non-terminal, so it can't avoid handling the non-terminal case anyway. less
actually uses isatty(3)
before checking the terminal dimensions, but isatty
works by trying a tty-only ioctl and checking for lack of error.
Even a simple pager like more(1)
(at least the util-linux version) has this feature, because it's probably the simplest sane behaviour to implement for that case.
Note that when you pipe something into less
(e.g. grep foo bar.txt | less
), it does have to open /dev/tty
for keyboard input. (You can see it do this with echo foo | strace less
).