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 but uninteded 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 old blog broke the string assigned to PS1
across multiple
lines, the current blog should have it all on one line. 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 (not active in the home directory) 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!