Setting $PAGER correctly

You would think that setting up a classical tool like more(1) nowadays should not be too difficult. Alas, the devil is in the details.

Up front, here are my requirements for a pager:

  • If I just scroll through the output, it should behave like cat(1). Consequently, it must not clear the screen and it must exit as soon as it reaches EOF.
  • It should display colour.
So far, so straightforward.

As much as I like to use POSIX utilities, more(1) doesn't fit the bill because it is specified to print control characters as implementation-defined multi-character sequences. Using non-standard switches like -R in order to modify its behaviour seems pointless, so I opted for less(1) instead.

Less has a couple of options to make it behave the way I want:

-E
Causes less to automatically exit the first time it reaches end-of-file.
-R
ANSI "color" escape sequences are output in "raw" form.
This sounds like it should do the right thing…

First of all, I tested my changes using the line

$ PAGER='less -ER' git log --all
Interestingly, it did not seem to make a difference whether I used the -R switch or not. As it turns out, this is because unless it is already set, git (and hg likewise) add the environment variable
LESS=FRX
when calling the pager. You can use PAGER=env to verify this. This means that for testing, it's actually better to use
$ git log --all --color | less -ER

As an aside, git and hg also set another variable which must be for a pager that I don't know:

LV=-c

Another stumbling block came when I actually tried this on one of my macOS systems. I had TERM set to xterm and output didn't appear at all if it was less than a screenful. This turned out to be because xterm's initialisation string switches to the alternate screen and clears it. Since my requirement is to work like cat(1), I don't need the alternate screen for this purpose. But I must admit that it's a nice feature when using vi(1), for example, so I don't want to change my TERM setting to ansi which doesn't have an alternate screen.

So the correct solution (which was already anticipated by git) is to also pass the -X option to less(1), causing it to ignore the terminal initialisation sequence.

And finally, unfortunately the meaning of the -c option is reversed on OpenBSD to what POSIX specifies for more(1), and clearing the screen is the default behaviour for less(1) on that platform. So in order to make my .profile work, I ended up using the code

if [ OpenBSD == "$(uname -s)" ]
then
    PAGER='less -ERXc'
else
    PAGER='less -ERX'
fi
export PAGER

Comments