45

Whenever I use a pager like less or an editor like nano in the shell (my shell is GNU bash), I see a behaviour I cannot explain completely and which differs to the behaviour I can observe with other tools like cat or ls. I would like to ask how this behaviour comes about.

The —not easy to explain— behaviour is that normally all output to stdout/stderr ends up being recorded in the terminal-emulators backbuffer, so I can scroll back, while (not so normally to me) in the case of using less or nano, output is displayed by the terminal-emulator, yet upon exiting the programs, the content "magically disappears".

I would like to give those two examples:

  • seq 1 200 (produces 200 lines in the backbuffer)
  • seq 1 200 | less (lets me page 200 lines, yet eventually "cleans up" and nothing is recorded in the backbuffer)

My suspicion is that some sort of escape codes are in play and I would appreciate someone pointing my to an explanation of this observed behavioural differences.

Since some comments and answers are phrased, as if it was my desire to change the behaviour, this "would be nice to know", but indeed the desired answer should be the description of the mechanism, not as much the ways to change it.

  • Since moving to Linux from Solaris, I've constantly been irked that when I exit from a man or vi command, I can no longer see what was on the screen. Thanks for asking this question that has bothered me! I hate doing a man vi | more ! – Mark Stewart Jan 11 '17 at 19:56
  • 1
    @MarkStewart You can do LESS=X man vi and vim "+:se t_ti= t_te=" (sorry, doesn't work on vi) for the behaviour you want. The first works because man typically uses less and -X causes less to not initiate the terminal's secondary screen. The second works by telling vim that the terminal escape sequences to initiate and end secondary screen are the empty string. – JoL Jan 12 '17 at 00:31
  • My version of Ubuntu Server does leave the nano text in terminal. – 56independent Sep 11 '21 at 19:42

3 Answers3

46

There are two worldviews here:

  • As far as programs using termcap/terminfo are concerned, your terminal potentially has two modes: cursor addressing mode and scrolling mode. The latter is the normal mode, and a program switches to cursor addressing mode when it needs to move the cursor around the screen by row and column addresses, treating the screen as a two-dimensional entity.

    termcap and terminfo handle translating this worldview, which is what programs see, into the worldview as seen by terminals.

  • As far as a terminal (emulated or real) is concerned, there are two screen buffers, only one of which is displayed at any time. There's a primary screen buffer and an alternate screen buffer. Control sequences emitted by programs switch the terminal between the two.
    • For some terminals, usually emulated ones, the alternate screen buffer is tailored to the usage of termcap/terminfo. They are designed with the knowledge that part of switching to cursor addressing mode is switching to the alternate screen buffer and part of switching to scrolling mode is switching to the primary screen buffer. This is how termcap/terminfo translate things. So these terminals don't show scrolling user interface widgets when the alternate screen buffer is being displayed, and simply have no scrollback mechanism for that screen buffer.
    • For other terminals, usually real ones, the alternate screen buffer is pretty much like the primary. Both are largely identical in terms of what they support. A few emulated terminals fall into this class, note. Unicode rxvt, for example, has scrollback for both the primary and alternate screen buffers.

Programs that present full-screen textual user interfaces (such as vim, nano, less, mc, and so forth) use termcap/terminfo to switch to cursor-addressing mode at start-up and back to scrolling mode when they suspend, or shell out, or exit. The ncurses library does this, but so too do non-ncurses-using programs that build more directly on top of termcap/terminfo.

The scrolling within TUIs presented by less or vim is nothing to do with scrollback. That is implemented inside those programs, which are just redrawing their full-screen textual user interface as appropriate as things scroll around.

Note that these programs do not "leave no content" in the alternate screen buffer. The terminal simply is no longer displaying what they leave behind.

  • This is particularly noticable with Unicode rxvt on some platforms, where the termcap/terminfo sequences for switching to cursor addressing mode do not implicitly clear the alternate screen buffer. So using multiple such full-screen TUI programs in succession can end up displaying the old contents of the alternate screen buffer as left there by the last program, at least for a little while until the new program writes its output (most noticable when less is at the end of a pipeline).
  • With xterm, one can switch to displaying the alternate screen buffer from the GUI menu of the terminal emulator, and see the content still there.

The actual control sequences are what the relevant standards call set private mode control sequences. The relevant private mode numbers are 47, 1047, 1048, and 1049. Note the differences in what extra actions are implied by each, on top of switching to/from the alternate screen buffer.

Further reading

Luc
  • 3,610
JdeBP
  • 68,745
5

A library called curses, it knows what terminal type you are using, and sends the correct escape sequences. There terminal is asked to switch to a different vertical buffer, and mode that allows more control.

heemayl
  • 56,300
  • 1
    One can use (or disable) the alternate screen without curses. – thrig Jan 11 '17 at 16:12
  • @thrig, you are right. running echo -en "\x1b\x5b\x3f\x31\x30\x34\x39\x68"; echo -en "\x1b\x5b\x33\x4a\x1b\x5b\x48\x1b\x5b\x32\x4a"; echo "hello"; sleep 1; echo -en "\x1b\x5b\x3f\x31\x30\x34\x39\x6c"; shows for instance the involved smcup rmcup escape sequences. In the particular cases of less and nano richard is nonetheless right, that the libcurses is employed (at least on my box). – humanityANDpeace Jan 11 '17 at 17:29
  • There's also the 'clear' system command (/usr/bin/clear on my system) which clears the screen. less might invoke this instead of linking with a full curses library. Though clear might be implemented using ncurses... – jamesqf Jan 11 '17 at 19:05
5

You can add a no-clear to this a couple of ways. You can resolve less from clearing the screen by calling it with the -X argument.

Note the $ symbol in the command lines below. This is specifying the terminal prompt of a normal user.

$ seq 1 200 | less -X

If that is your desired behavior you can alias less to this default with:

$ alias less='less -X'

There are similar workarounds for other programs.

Alternatively, rather than trying to configure each application individually you can add your own terminal definition. In this case I'll call it xterm-noclear for this example.

Run these steps to create a new xterm definition:

$ infocmp -I xterm > xterm-noclear.src
$ gedit xterm-noclear.src

Now change the second line in your editor from xterm to xterm-noclear.

Remove the instructions that clears the screen by searching rmcup and smcup, and removing these two instructions:

smcup=\E[?1049h,

and

rmcup=\E[?1049l, 

Save the file then add the terminal definition with:

$ tic ~/xterm-noclear.src

You can make this a system-wide available terminal definition with:

$ sudo tic ~/xterm-noclear.src

Now you can use this TERM with:

$ export TERM=xterm-noclear
L. D. James
  • 1,604
  • Thanks! By removing "smcup=..." and "rmcup=..." lines I was able to exit "gnu nano", "vi" and "less" application without clearing the screen after exit, so I can see contents of edited files in my bash. But I used "infocmp -I xterm-256color" so I could have RGB colors in my terminal. It works even inside "screen" and "tmux" when I do "export TERM=xterm-256color-noclear" (my new terminal configuration that works!). – Rodrigo De Almeida Siqueira Mar 31 '23 at 22:11