21

When I uss less file1 file2 I get both files shown in the "less buffer viewer", but less file1 file2 | cat prints the content of both files appended to stdout. How does less know if it should show the "less buffer viewer" or produce output to stdout for a next command? What mechanism is used for doing this?

2 Answers 2

31

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'
6
  • 1
    @tfh If STDOUT is not attached to a pipe or a redirection, it's correct that they don't print that STDOUT is attached to a pipe or a redirection. Put all three in a script. Call bash script.sh, bash script.sh | cat, bash script.sh > file, and see what output you get.
    – hvd
    Commented Dec 30, 2016 at 13:10
  • 1
    stdout isn't something that can be "written to a file". It's something you write() to. less doesn't have to do anything differently depending on whether its output is a file, pipe, socket, or block device, or whatever. It only matters that it's not a tty, so it just behaves like cat. (I assume you knew this and just chose the wrong words to explain it, but I thought I'd point this out for other readers). Commented Dec 31, 2016 at 4:31
  • So you mean it is the task of less to behave like cat in my specific question - or more general: behave like the next command in a pipeline. From what I understood I can not assume that the exact same behaviour is implemented in a different tool also.
    – tfh
    Commented Dec 31, 2016 at 10:07
  • @tfh: No, less doesn't "figure out" that cat is next. It just behaves like cat regardless of what it next, if it's stdout is not a tty. Commented Dec 31, 2016 at 11:18
  • @MichaelD.: thanks, corrected my answer. I just guessed that less would go ahead and use one TCGETS to get the terminal dimensions or discover that it's not a tty, but apparently I guessed wrong. Commented Dec 31, 2016 at 11:26
7

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).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .