While it looks like a throwback to the 1980s or even the late 1970s, the terminal is a very capable way of performing tasks on a POSIX (BSD, GNU/Linux, etc.) computer. Some tasks are simply better suited to a Text User Interface (TUI) than a Graphical User Interface (GUI) that runs under X or Wayland. Even better, a terminal application is included as part of the various GUI desktops or for the minimalist, stand-alone terminals such as XTerm or URxvt are available. On hardware too limited to run a GUI well, the console is always available and TUI and Command Line Interface (CLI) programs are the rule. I will differentiate between a TUI program that often runs in full screen mode such as top
or Midnight Commander or a CLI program that requires various arguments to control its behavior such as ls
or cp
. Additionally, TUI programs often display a limited set of psuedo-graphics character such as lines, tees, and corners to draw boxes and such on the screen.
Modern terminals and the console do support various capabilities that provide a visual enhancement to the characters displayed on the screen including multi-language character sets such as UTF-8 (not to be confused with the amateur radio digital mode FT8), color, bold, underline, and italics. All of these good things do come at a price (don’t they always?) and that price is backward compatibility. That is where terminfo and tput
enter our vocabulary.
Terminals have a long history in POSIX/Unix from the earliest days as a more-or-less specialized printer to a terminal featuring a glass teletype display and keyboard to today where terminals are a software emulation presented on our monitor’s screen. All of this baggage history carries forward to the present day. In some ways the backward compatibility is a blessing and a curse! Which leads us to curses, a programming library that gives programs access to terminal control functions through a uniform programming interface. The current project is ncurses or “new curses” and is the version found on almost all POSIX systems today. The ncurses project is the home of the terminfo library and tput
.
With that bit of history out of the way, tput
and its use in ~/.bashrc
will be the focus of this article.
Escape sequences
The first way of adding color to the shell prompt or to manual pages is often learned by way of ANSI escape sequences. Escape sequences are fun to play with and much can be learned about the basics of terminal behavior through their use. The following screenshot shows the sequences to print text in color:
In this case I added the bold escape code to distinguish the word “White” from the normal darker white (gray) of “and” (click on the image for a larger view). Many resources for learning about these codes and their use can be found on the Web and a good one is FLOZz’ MISC.
While simple to implement, the sequences do have a drawback that advanced effects that work with a given terminal emulator may not work with the next. Besides not working as expected the unrecognized sequence will likely just be printed as normal text or some special effect code will cause a different effect entirely. In short, the ANSI escape sequences are not portable. If you always only use a given terminal emulator this isn’t much of a problem Wanting to use an effect like underlining or italics may work well with your desktop’s terminal application but will fail on the Linux console, for example.
Enter tput
The tput
utility gives us a portable way of querying the terminfo database to determine what escape sequence, if any, exists for a desired character attribute. For example, Xterm and terminals that set the TERM
environment variable to xterm
support printing text with the italics attribute. The Linux console, however, does not support italics.
Consider the following. In my ~/.bashrc
I have the following line which assigns the escape sequence for setting the italics attribute to a shell variable:
ITA=$(tput sitm)
In an Xterm terminal the variable is assigned the following escape sequence:
$ set | grep 'ITA='
ITA=$'\E[3m'
While in the Linux console the variable is not assigned any value:
$ set | grep 'ITA='
ITA=
With hardcoded escape sequences in ~/.bashrc
at best the sequence would be ignored by the Linux console and at worst the sequence characters would be displayed or some interesting effect might happen. In other words, the behavior is undefined.
Here we see green text instead of the expected italics on the Linux console when a raw escape sequence is used:
With the use of tput
my ~/.bashrc
is now portable to various Xterm type terminals and the Linux console. As the variable has no assigned value on the Linux console, wherever it is used an empty string (in other words nothing) will be inserted in its place.
tput
in ~/.bashrc
The following assignments are in my ~/.bashrc
:
# Use variables set from terminfo capabilities to make PS1 and LESS_TERMCAP_*
# variables less cryptic.
#
# Colors: 0, Black; 1, Red; 2, Green; 3, Yellow; 4, Blue; 5, Magenta; 6, Cyan; 7, White.
# Foreground (text) colors
BLK=$(tput setaf 0)
RED=$(tput setaf 1)
GRN=$(tput setaf 2)
YEL=$(tput setaf 3)
BLU=$(tput setaf 4)
MAG=$(tput setaf 5)
CYA=$(tput setaf 6)
WHT=$(tput setaf 7)
# Background colors
BLKB=$(tput setab 0)
REDB=$(tput setab 1)
GRNB=$(tput setab 2)
YELB=$(tput setab 3)
BLUB=$(tput setab 4)
MAGB=$(tput setab 5)
CYAB=$(tput setab 6)
WHTB=$(tput setab 7)
# Character attibutes
BLD=$(tput bold)
ITA=$(tput sitm)
NOR=$(tput sgr 0)
and my PS1
is set as:
PS1='(\[$CYA\]$SHLVL\[$NOR\])[\[$YEL\]\j\[$NOR\]]\[$GRN\]\u\[$NOR\]@\[$BLD$GRN\]\h\[$NOR\]:\[$BLD$BLU\]\w $(__git_ps1 "\[$NOR\](\[$MAG\]\[$ITA\]%s\[$NOR\])")\n\[$BLD$WHT\]\$\[$NOR\] '
Note: The blog is breaking the string assigned to PS1
across multiple lines. In ~/.bashrc
it should all be on one line.
PS1
is the primary prompt as seen in the screenshot above. As a bonus some text is appended at the end of the path that shows what branch I am working in whenever I am in a Git repository’s working tree as shown in the screenshot below:
Some other notes about my prompt. The first value in parentheses is the shell level. This value will increment if a prompt is opened in a sub-shell. The next value in brackets is the number of jobs that are running in this shell. Remember that processes can be put into the background, often with the
Ctrl-Z
key combination. Next is my username @ hostname. I use this prompt on other machines and with virtual machines with various colors so this helps keep things straight. Then the path and finally the Git branch if in a repository working tree.
Unifying the colors of manual pages
Without some modification the colors and styles of text used in displaying manual pages through the less
utility will differ between an Xterm and the Linux console. To make them appear as close as possible I have the following variables set in my ~/.bashrc
:
# Use terminfo capabilities to styleize man pages.
#
# See:
# https://unix.stackexchange.com/questions/119/colors-in-man-pages
# https://unix.stackexchange.com/questions/108699/documentation-on-less-termcap-variables
export LESS_TERMCAP_md=$BLD$WHT # enter double-bright mode - bold white
export LESS_TERMCAP_me=$NOR # leave double-bright, reverse, dim modes
export LESS_TERMCAP_so=$BLD$CYA$BLUB # enter standout mode - bold cyan on blue background
export LESS_TERMCAP_se=$(tput rmso)$NOR # leave standout mode
export LESS_TERMCAP_us=$ITA$CYA # enter underline mode - italics, cyan
export LESS_TERMCAP_ue=$(tput ritm)$NOR # leave underline mode
export LESS_TERMCAP_mr=$(tput rev) # enter reverse mode
export LESS_TERMCAP_mh=$(tput dim) # enter half-bright mode
export LESS_TERMCAP_ZN=$(tput ssubm) # enter subscript mode
export LESS_TERMCAP_ZV=$(tput rsubm) # leave subscript mode
export LESS_TERMCAP_ZO=$(tput ssupm) # enter superscript mode
export LESS_TERMCAP_ZW=$(tput rsupm) # leave superscript mode
This is a mixture of previously assigned variables and one-time calls to tput
with a given attribute. In addition I have the following man
specific variables set to control the width of the displayed page and some additional settings for less
:
# Set maximum width of man pages to 80 characters
export MANWIDTH=80
export MANPAGER='less -s -M +Gg'
The following screenshots show the same manual page in an Xterm and on the Linux console:
As can be seen, the Xterm shows the cyan text in italics while the Linux console does not. Still, this goes a long way toward having the most consistent color styling for manual pages across the various terminal types.
What else?
Various Web searches for tput
will turn up many ways it can be used in shell scripts to do many affects with text and cursor control. This is just a small example of the capability of this utility. Explore and have fun!