Coloring Terminal Text: tput and ANSI Escape Sequences

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!

Table illustrating the color and text attribute codes that may be used in an ANSI escape sequence, displayed in a dark-themed terminal on a black background.
Table of ANSI escape sequence codes in the GNOME terminal under Ubuntu (dark theme)
Table illustrating the color and text attribute codes that may be used in an ANSI escape sequence, displayed in a light-themed terminal on a white background.
Table of ANSI escape sequence codes in the GNOME terminal under Ubuntu (light theme)

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

Comments

Related Posts

Why I No Longer Write Articles About 42 School Projects

Following a meeting with 42 school’s pedagogical team, I decided to remove all articles directly related to 42 projects.

Read More

Pipe: an Inter-Process Communication Method

By default, it is difficult to get two processes to communicate with each other.

Read More

Handling a File by its Descriptor in C

The available system calls to create or open, read, write, and delete a file in C all make use of a file descriptor.

Read More