Ken Muse

Coloring in ANSI


I recently received an interesting challenge – get colored logging output using PowerShell. Normally, this is a simple task in PowerShell. We specify the foreground and background colors as part of Write-Host:

1Write-Host "This is colored text" -ForegroundColor Red -BackgroundColor White

Unfortunately, that doesn’t always work – especially if you’re writing to logs from a Runner. The reason is that the outputs have been redirected. When PowerShell’s output is redirected, the raw text is passed to the output stream and no colors are included in the output. If we want color, we have to create it ourselves. The trick to doing this is called ANSI escape sequences.

Escape sequences are a series of encoded instructions that the console interprets to position the cursor or change the color of the output. These are structured very similar to function calls. They begin with marker bytes which indicating the start of a sequence, an escape character (0x001B) followed by a sequence indicator character. This marker is typically denoted is ESC, since it is an unprintable character. For coloring, we care about the Control Sequence Introducer (CSI), which is an open square bracket ([, or 0x009B). The next characters are essentially he parameters to a function, which ends the sequence. For setting the color, the function is Select Graphics Rendition (SGR), which is indicated by the letter m. If there are multiple parameters for the function (or multiple actions being taken), they are separated with a semi-colon. These are generally a series of ASCII numerals.

The basic sequence is therefore ESC[arg1;arg2;arg3m.

The arguments can represent multiple state changes. For example, they can mark text as bold or italic, or set a base color. Some common arguments:

ValueDescription
0Reset all values
1Bold
3Italic
4Underline

Colors can be set a few different ways. The easiest is to use an 8-color pallette:

ValueDescription
30Black
31Red
32Green
33Yellow
34Blue
35Magenta
36Cyan
37White

To use these as a background colors, add 10 (40-47). A bright version of the colors is also available by adding 60 (foreground, 90-97) or 70 (background, 100-107).

In truth, each code can be thought of as instructions to be executed, followed by additional parameters (if required). Each instruction is then executed in order. An example of this is setting RGB color values. This requires the sequence 38;2;R;G;B (foreground) or 48;2;R;G;B (background). Simply replace R, G, and B with values from 0 to 255. For example, the color “goldenrod” is #DAA520, or RGB (218, 165, 32). The escape sequence for setting this as a foreground color would therefore be ESC[38;2;218;165;32m.

Combining this together, we can string together multiple values to format and display text. For example, if we want to display “This is red” in bold with a red foreground and goldenrod background:

ESC[1;31;48;2;218;265;32mThis is red

It would appear as This is red.

The sequence can be explained as follows:

ESC[1;31;48;2;218;265;32mThis is red
CSIBoldRed ForegroundBackground RGBRGB(216,165,32)Execute SGR function and end sequencePlain text

To reset this back to the defaults, we use the reset sequence:

ESC[0m

And that’s all there is to creating ANSI sequences. And it’s just the start. Other sequences can be used to move the cursor, erase the display (or a line), and more.

I leave those as an exercise to the reader.