Coloring Terminal Text: tput and ANSI Escape Sequences
- Mia Combeau
- Shell | Unix
- August 8, 2022
Table of Contents
A terminal with black-on-white text or vice versa is not very interesting or attractive or informative. Thankfully, modern terminal emulators offer a variety of text styling options as well as foreground and background colors. It’s just a matter of knowing how to apply them with ANSI escape sequences or the tput
command. We will explore both methods in this article.
How a Terminal Formats Text
A terminal is an interface: its only function is to display text. It prints whatever it receives from the standard input, the standard output or the standard error. Typically, the shell provides a command prompt to print and as the user types a command, the terminal then prints each typed character. While the command executes, the terminal might receive an output to print from whatever program is currently running. A previous article explains the difference between a terminal, a console and a shell.
In order to be able to print anything, the terminal needs a font and a color that contrasts with its background. Plus, its users appreciate the option of displaying text in a different color or style. Being able to display a critical error message in red or to simply underline a link can be very useful.
In truth, the terminal doesn’t decide what font style or color to display on its own. It inherits them by default from a theme, its own or the operating system’s. The shell indicates which font style and color, if any, should be used to display the command prompt. All other colors come from either the shell or the programs that provide text to print.
Since the terminal only ever deals with text, programs that wish to specify the way a text should be printed must find a way to communicate the desired formatting in the text to be printed itself. That’s where ANSI escape sequences come in, as well as the tput
command.
What is an ANSI Escape Sequence?
ANSI escape sequences are a standard that allows a terminal to receive information, not only about font styles and colors, but also the cursor’s position and many other terminal options. In order to encode this information, a distinct sequence of characters is directly embedded into the text it receives. The terminal interprets these sequences not as characters to print, but as commands.
An ANSI escape sequence always starts with an escape character, which can be written in one of three ways:
\e
\033
\x1B
If we take a look at the ASCII table, we can see that these three notations all literally correspond to the 27th ASCII character, ESC (escape). The first is its textual notation, the second is its octal number, and the third, its hexadecimal number.
In most cases, the escape characters is followed up with an opened bracket “[”. Together, these two bytes constitute the CSI, the “control sequence introducer”.
Here is the complete ANSI control structure:
"\e" + "[" + <numbers separated with ";" (optional)> + <letter>
We could think of a control sequence like this as a function call, where the letter at the end is the name of the function and the optional numbers in the middle are the parameters. With this interpretation, we could read a sequence like " \e[1;42m
" like this:
\e[ # function call
1;42 # function parameters (1, 42)
m # function name
These ANSI escape sequences can be used pretty much anywhere, in our programs, but also on the command line. To use an ANSI escape sequence in an echo
command for example, we need to add the option -e
so that the sequence can be interpreted:
echo -e "With -e, \e[1m<--this and that-->\e[0m is interpreted"
We will later see which escape sequences allow us to control text display.
How Does the tput Command Work?
On the command line or in a Bash script, it might be preferable to use the tput
command to indicate how a text should be displayed. This command uses the terminal’s terminfo to automatically find the correct escape sequence for a certain text effect. Terminfo is a database that describes all of the capabilities the terminal has: which operations it can do, how to perform them, and which initialization sequences it requires. To view the contents of terminfo, we can use the infocmp
command.
Each one of the terminal’s capabilities has a name. For example, bold describes the terminal’s capability to apply the bold attribute to text. So through an echo command, we can emphasize a piece of text this way, for instance:
echo "This text is $(tput bold)important"
The tput
command will then find the right sequence in the terminfo to put the text in bold. Of course, these sequences are standardized thanks to ANSI escape sequences. However, tput
does have two advantages: it can apply a capability even if the terminal does not comply with the ANSI standard, and it informs us if the terminal does not have the requested capability.
Indeed, each terminal is different and not all have the same capabilities. Most will be able to print text with basic font styles like in bold or italics, or with elementary colors like red, green and blue. But many do not support uncommon typographical attributes like strike-through or blink. Some don’t know any other color than black and white ! To see how many colors a terminal supports, we can use the tput colors
command.
Styling Text in the Terminal
As we’ve seen, we can control the way in which the terminal displays text thanks to the tput
command or ANSI escape codes. The following tables describe the codes for each method.
Regarding ANSI escape sequences, the letter to indicate at the end is “m”, which signals that we intend to control a graphics mode, i.e. text styling. If we wanted to control the cursor position, for example, we would use the “H” letter which corresponds to the cup
capability in terminfo. Several other letters exist to control all sorts of other terminal options, but this article will concentrate only on text formatting.
Changing Text Attributes
There are several text attributes we can request to control a text’s font style. The following table shows both the ANSI code to add to the escape sequence as well as the terminfo capability name (“TermCap”) to pass to the tput
command to achieve the described effect.
ANSI Code | TermCap | Description | Example |
---|---|---|---|
0 | sgr0 | Reset all attributes or Normal | |
1 | bold | Bold or Bright | |
2 | dim | Dim | |
3 | sitm | Italics | |
4 | smul | Underlined | |
5 | blink | Blinking | |
6 | blink | Blinking | |
7 | rev | Inverted or Reversed | |
8 | invis | Invisible | |
9 | smxx | Strike-through |
If we want a bold text, the full ANSI escape sequence will be: \e[1m
. We can reproduce the same effect with the tput
command in the following ways:
tput bold && echo hello
echo "$(tput bold) hello"
So, does this mean we can combine all of these attributes to make text bold, dim, italics, blinking, inverted and crossed out? That’s a bit extreme, but the answer is yes, we can do all of that in one ANSI escape sequence if we separate each attribute with semicolons:
echo -e "\e[0;1;2;3;4;5;6;7;9mHello\e[0m"
Needless to say, we can’t include the “invisible” attribute (8) if we want to see all the other effects. However, the invisible attribute (8) goes very well with the reverse attribute (7) in order to highlight the fact that there is text even though it is redacted, for example.
The order in which we provide the attributes does not matter, except for attribute 0. The 0 attribute resets all other style attributes and returns text to its default styling. Of course, if we put the 0 attribute at the end of our previous ANSI sequence, before the “m” and after all other attributes, the effects of those attributes will all be cancelled! That said, it is important to remember to add the \e[0m
escape sequence at the end of our line or whenever we want the text to go back to normal. Otherwise, these style attributes will probably affect all of the following lines, until the terminal finds a \e[0m
sequence.
The tput
command has the same effect: we shouldn’t forget to add a tput sgr0
to cancel all of the previously set text attributes. However, setting several text attributes at once is a little more involved with tput
. For example, to style a text with all of the attributes as we did earlier, we’ll have to call tput
several times in a row:
tput bold && tput dim && tput sitm && tput smul && tput blink && tput rev && tput smxx && echo Hello
In order to request several styling options in the same command call, we will have to use tput
with the -S
option. Then, we’ll have to input a list of capabilities, one per line, terminated with an exclamation mark, in the standard input in order to get the result we want:
$ echo "$(tput -S)Hello$(tput sgr0)"
> bold
> dim
> sitm
> smul
> blink
> rev
> smxx
> !
Changing Foreground Text Colors
The following table presents the most common ANSI codes and terminfo capability names used to control the foreground color, the color of the text itself.
ANSI Code | TermCap | Color | Example |
---|---|---|---|
30 | setaf 0 | Black | |
31 | setaf 1 | Red | |
32 | setaf 2 | Green | |
33 | setaf 3 | Yellow or Brown | |
34 | setaf 4 | Blue | |
35 | setaf 5 | Purple or Pink | |
36 | setaf 6 | Cyan | |
37 | setaf 7 | Grey |
These colors all have brighter variations. The ANSI codes for those variants go from 90 to 97, and their terminfo capability equivalents are setaf
8 to 15. This capability name, setaf
, stands for “set a foreground”, meaning the text color.
Of course, we can combine a text color with a font style like bold or italics. It’s the same process that we saw earlier, all we need to do is add the color attribute to all of the other text attributes. For example, \e[34;4m
(or \e[4;34m
) will show the text in blue and underlined. While it is possible to specify several colors for the same text, only the last specified color will be applied. The \e[0m
sequence cancels all color attributes as well.
The actual color a terminal displays varies, particularly with intermediary colors. Primary colors like red, green and blue are usually the same no matter the terminal. However, purple can often look more like pink and yellow might look brown.
However, with ANSI escape sequences, we can indicate a very specific RGB color, as long as the terminal is able to display 256 colors (check the terminal colors with tput colors
or tput longname
).
ANSI Code | Description |
---|---|
38;2;r;g;b | Sets a text color with RGB values. For example: \e[38;2;255;0;255m |
I haven’t found an equivalent to this in the terminfo capabilities, although it must exist. Please leave a comment if you know it, I’ll be happy to learn which capability allows us to specify RGB values with the tput
command.
Displaying a Text Background Color
The method to apply background colors is the same as foreground colors. Here is the ANSI code and terminfo capability table for background colors.
ANSI Code | TermCap | Color | Example |
---|---|---|---|
40 | setab 0 | Black | |
41 | setab 1 | Red | |
42 | setab 2 | Green | |
43 | setab 3 | Yellow or Brown | |
44 | setab 4 | Blue | |
45 | setab 5 | Purple or Pink | |
46 | setab 6 | Cyan | |
47 | setab 7 | Grey |
Just as text colors, there are more vivid versions of these background colors. For ANSI escape sequences, the codes for the brighter background colors range from 100 to 107, and their terminfo capability equivalents are setab
8 to 15. The name " setab
" is also an abbreviation, this time for “set a background”.
As always, we can also combine background colors with foreground colors and other text attributes. For example, \e[1;34;3;43;4;5m
will display bold, italic, underlined, blinking blue text on a yellow background. Splendid ! However, only the last specified background color will be displayed: it’s impossible to combine several background colors together. As with all other text attributes, a background color stops when another one replaces it or with the \e[0m
escape sequence.
In the same way as foreground text colors, we can specify any RGB background color with this ANSI escape sequence:
ANSI Code | Description |
---|---|
48;2;r;g;b | Sets a background color with RGB values. |
For example: \e[48;2;255;0;255m
The name of the equivalent terminfo capability for this functionality eludes me as well.
Displaying the Styling Capabilities of a Terminal
As mentioned, terminal emulators don’t all have the same capabilities. Regarding colors, most terminals can display at least 8 basic colors for the foreground and background. Some offer up to 256 colors, while others cannot display anything other than black and white.
For font styling, most emulators are able to make a text bold, italic, invisible or reversed. However, ANSI escape sequences don’t always have the same exact effect in different terminals: what one terminal interprets as bold, another interprets as bright. Therefore, in the first terminal, bold and dim are compatible, whereas in the second, they are not.
So how can we see a terminal’s formatting capabilities and how it displays the different text attributes? In order to display the behavior of the same ANSI sequences in different terminals, all we need to do is write a small program, like this one in C:
#include <stdio.h>
int main(void)
{
int n;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
n = (10 * i) + j;
printf("\e[%dm %3d \e[0m", n, n);
}
printf("\n");
}
return (0);
}
The following images show the result of this little program in the GNOME terminal in Ubuntu. They illustrate the effects of all the ANSI escape sequences regarding text styling. Surprisingly, we’ve just discovered something new: in this terminal, the \e[21m
sequence underlines the text twice!
If we compare these two results, we might notice that the theme of the terminal, light or dark, has a major effect on the legibility of certain color combinations. In a dark-themed terminal, the text with a black color (30) is totally invisible. In the light-themed terminal, the grey colors (37 and 97) are difficult to read. So we need to curb our coloring enthusiasm if we want to display specific colors in the outputs of programs we intend to distribute to other users. Not everyone uses the same theme we do!
That’s it for text styling in the terminal! If you know of other ways to control text display in the terminal, please let me know in the comments.
Sources and Further Reading
- Wikipedia, ANSI escape code [ Wikipedia]
- Bash Hackers Wiki, Terminal codes (ANSI/VT100) introduction [ bash-hackers.org]
- Zeste de Savoir, Termcap et Terminfo, [ zestedesavoir.com]
- Burke Libbey, Everything you never wanted to know about ANSI escape codes[ burke.libbey.me]
- William Shotts, Jr., tput [ linux-commands.org]
- Linux Programmer’s Manual :