Pieter Kraamwinkel

Blog

ANSI escape codes

Posted on

While looking for a way to detect the arrow keys within a command line application, I stumbled upon ANSI escape codes: codes that allow you to perform operations within the CLI. I was able to find out how to detect the arrow keys, but decided to dive a bit deeper in what these codes do. In this article I will explain what these codes are, and how to use them

What are ANSI escape codes?

Before there was a standard for which key sequences executed which commands, vendors all had their own implementations. To make life easier for everyone, ANSI created a standard in 1976 so developers could all target the same API.

Terminal support

From what I understand (I did not test it) these codes only work in all popular terminals except cmd.exe and PowerShell in Windows. From Windows 10 onwards you seem to be able to get it to work with fiddling a bit with Windows Registry, but I will not focus on that here.

I will be using GNOME Terminal on Ubuntu 18.04.

Trying out some ANSI codes

Open your terminal and type "echo hello world" without hitting enter.

bash
echo "hello world"

Now hit these keys in order while your terminal is focused and your cursor is still at the end of the command: ESC (escape), [ (square bracket open), 5 and finally D. Your cursor should have moved 5 places to the left, on the letter "w" of "world"

This sequence, ESC[5D, is an ANSI escape code and there are a lot more of them!

Sadly a lot of them do not work in the command line itself. They need to be printed. One easy way to do that is with our beloved echo.

The escape codes used in this blog post

There are a lot of different escape codes you can use, and in the sources at the end of the post you can find multiple sites with most of the codes. Here I want to focus on only a selected few, to keep it coherent.

Some commands require integer parameters. The placeholders for these parameters are enclosed with < and >. The parameters are mostly, if not all, optional, and if they are not given a default of 0 or 1 is used instead, whichever is the lowest allowed value.

Spaces here are for readability. The commands themselves may not have any spaces in them.

plain
ESC [ <count> A = Move cursor <count> places up
ESC [ <count> D = Move cursor <count> places to the left
ESC [ <mode> J = Remove text. <mode> 0 erases all text below, <mode> 1 erases all text above, and <mode> 2 erases all text.
ESC [ <row>;<column> H = Move cursor to <row> and <column>
ESC [ <mode1>; <mode2>;...m = Apply zero or more graphic modes, like changing the text  colour or making the text blink. Modes can be found at https://stackoverflow.com/revisions/33206814/3 

Again, these are not all the codes, but these are the ones we are going to use here.

Using echo

The escape character is not a character you can type. We need to find a way to feed it into echo anyway. Luckily the man pages can help us out! man echo gives us the following:

man
If -e is in effect, the following sequences are recognized:

       \\     backslash

       \a     alert (BEL)

       \b     backspace

       \c     produce no further output

       \e     escape

Let’s try the following escape code which clears the screen for us:

bash
echo -e "\e[2J"

Note that it only clears the screen. It does not move the cursor up, for that the escape code "ESC[<row>;<column>H" is needed. Together they form:

bash
echo -e "\e[2J\e[H"

You will see that after executing the echo above, the prompt will bring you on line 2 instead of line 1. That is because the echo inserts a newline character after executing. To prevent that from happening, you need to add the n-flag:

bash
echo -en "\e[2J\e[H"

Hello world

Lets now create a sequence that will place a blinking, bold and underlined "Hello world" in blue on a white background, with a few empty lines below it. Let also place it a bit away from the terminal border.

First we need to clear the screen, so we need \ESC [ 2 J.

Secondly we want to move the cursor a bit away from the top left border. If we want to move the cursor an equal amount from the top border and the left border, we have to move the cursor twice as much from the left border as the size of characters have a 2:1 ratio. To have a distance of 1 height of a character away from the top left borders, we need \ESC [ 2; 3 H.

Now we have to apply our markup: bold is 1, underline is 4, blinking is 5, blue foreground colour is 34 and white background is 47. Together they form \ESC [ 1; 4; 5; 34; 47 m

Then we have our text, followed by \ESC [ m to undo the formatting from the previous step.

In the end we have the following code:

bash
echo -e "\e[2J\e[2;3H\e[1;4;5;34;47mHello world\e[m\n\n\n"

Sources and further reading

https://stackoverflow.com/a/38553473

https://stackoverflow.com/a/33206814

https://en.wikipedia.org/wiki/ANSI_escape_code

https://docs.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences

https://vt100.net/docs/vt220-rm/contents.html

https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Controls-beginning-with-ESC

https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-48,%202nd%20Edition,%20August%201979.pdf