Skip to content
Select theme

How to colour terminal output

Short answer: Print ANSI escape sequences around the text (\x1b[31m starts red, \x1b[0m resets) and leave them out entirely when the NO_COLOR environment variable is set.

From a fresh project — cargo new colour — replace src/main.rs with each block below and run it with cargo run -q. A terminal renders the escapes as colour, so to see the bytes themselves pipe the output through cat -v, which prints ESC as ^[.

$ cargo run -q | cat -v
^[[31mred^[[0m

\x1b is the escape character, byte 27, and [31m is a control sequence: [ opens it, 31 is the parameter, and m is the command that sets colour and style. 31 means foreground red. The trailing \x1b[0m resets everything to normal, and forgetting it is the classic bug:

$ cargo run -q | cat -v
^[[31mred
still red

The colour is a state change, not a wrapper. Nothing in the second line resets it, so a terminal keeps painting red until it meets a reset.

$ cargo run -q | cat -v
^[[1mbold^[[0m
^[[31mred^[[0m
^[[1;32mbold green^[[0m
^[[4;34munderline blue^[[0m

Several parameters go in one sequence, separated by ;, so 1;32 is bold and green at once. The codes worth remembering: 1 bold, 4 underline, 30–37 for the eight foreground colours (31 red, 32 green, 33 yellow, 34 blue), 40–47 for the same eight as backgrounds, and 90–97 for the bright variants. paint returns a String instead of printing, so it drops into any format!.

Some readers pipe output into a pager, a log file, or a screen reader, where escape codes are noise. The informal standard at no-color.org asks software to check for one variable: when NO_COLOR is present and not an empty string, don’t add colour.

$ cargo run -q | cat -v
^[[32mok^[[0m done
$ NO_COLOR=1 cargo run -q | cat -v
ok done
$ NO_COLOR= cargo run -q | cat -v
^[[32mok^[[0m done

env::var returns Ok for a variable that is set at all, empty or not, so the check has to look at the contents too — the standard’s words are “present and not an empty string (regardless of its value)”. That is why NO_COLOR= still colours and NO_COLOR=0 switches colour off: the value does not matter, only that there is one. Read the variable once at startup and pass the flag down; checking it inside paint re-reads the environment on every call.

Two sequences and a flag is all most programs need. A crate earns its place when styles have to compose (bold red on black), when 256-colour and truecolor values matter, when the console is Windows, or when the output might not be a terminal at all. owo-colors, colored and termcolor are the names you’ll meet. Whether the output is a terminal is the next step in this chapter.