בס״ד

Using ANSI Color Codes to Colorize Your Bash Prompt on Linux

2012-11-19 13:32

The goal is to create a design similar to the following example, but with more artistic merit. To accomplish this, we must understand several things, including: special characters, some bash scripting, and ANSI colors.

╔ǁ * ǁ 08:24 AM Fri Aug 17 ǁ 0 ǁ michaelplotke@work ǁ ~/Desktop ǁ 11 files ǁ═╗
╚═»

Box-Drawing Characters

Unicode provides an extensive range of symbols and special characters we can use to make our Bash prompt awesome. In my case, I am using the special box drawing characters to add cutesy little arrows and double lines.

The Bash PS1 Variable

All of the content and the appearance of the Bash prompt is managed by the PS1 variable, which is defined in ~/.bashrc for each user, and in /etc/*bashrc for users without their own definition.

While the PS1 variable can become incredibly complex, it can also be very simple. Here is the default that came with my Arch Linux install:

PS1='[u@h W]$ '

This creates a simple prompt which displays:

[michaelplotke@work Desktop]$

Let's discuss how that works.

Some PS1 Codes

These are generally called Bash prompt "escape sequences", as they consist of a backslash (the standard escape character) followed by a regular character. Although there are many available, I will just list the one's I've used:

u - the current user's username h - the current machine's hostname j - the number of jobs managed by this shell @ - the current time (12 hour format) d - the current date w - path of the current working directory W - just the current working directory e - an ASCII escape character (033) n - adds a newline character

Summary

We can now clearly see how '[u@h W]$ ' becomes [michaelplotke@work Desktop]$ because it makes use of some basic PS1 codes plus some plain text. No problem.

Checking State

There are two states I want to track. The exit status of the previously run command, and whether or not I'm logged in as root. In the example above the PS1 is aware of these circumstances. Here's how:

Exit Status

In Linux, an exit code of anything other than 0 generally indicates some kind of error. To get the exit code, we need to take a look at the $? variable. Try running various commands, then running echo $? right after from the same prompt. This should be 0 if the command completed successfully, and something other than 0 if it didn't.

For our purposes, we need to test the exit status. We can use the form if [ $? -eq 0 ]; then echo 'good'; else echo 'bad'; fi replacing the echo commands with whatever we want to do in each case.

Root Prompt

In Linux, the root user receives the user id 0. The current user id for a given prompt is stored in the variable $EUID. Thus we test for root with if [ ${EUID} -eq 0 ]; then echo 'root'; else echo 'user'; fi.

Gathering Data

Linux is blessed with a plethora of fast little command line utilities, perfect for gathering data to use in your Bash prompt. While I'm only getting the number of files, there are many other options, for example git information.

The PATH variable should be available when the prompt is contructed, so you should be able to use command exactly as you would in the terminal.

Number of Files

I first use ls -1 to generate a list of all the files (and folders, which are also called files in Linux) in the current directory. I then use | to pipe (pass) that data to wc -l which counts the number of newlines. This provides the number of files in the current directory.

The complete command is ls -1 | wc -l

ANSI Colors

There are 16 ANSI Colors, which are actually 8 colors, each having "normal" and "bright" intensity variants. The colors are black, red, green, yellow, blue, magenta, cyan, and white. In certain circumstances bright intensity could be actually brighter, or, in the case of an xterm, it could be bold.

                 40m     41m     42m     43m     44m     45m     46m     47m
     m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
    1m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
   30m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
 1;30m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
   31m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
 1;31m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
   32m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
 1;32m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
   33m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
 1;33m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
   34m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
 1;34m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
   35m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
 1;35m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
   36m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
 1;36m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
   37m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  
 1;37m   gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw     gYw  

Usage Syntax

To set a font to the color and style desired, we use the syntax \033[#m where # can be a valid set of semicolon separated numbers. The \033[ is a control sequence initiator which begins an escape sequence. In our case the sequence is a set of "m" terminated select graphic rendition parameters for rendering the text as desired. More details.

If that makes no sense, just concentrate on which numbers to use. Font color makes use of 30 to 37. Background color makes use of 40 to 47. A 0 (or no value given) for the font color resets it to the default, which depends on the setup. For additional rendering options, 1 sets the font to bright / bold, 4 sets it to underlined, 7 sets it to negative colors.

It might help to test this by running this command.

echo -e "testing \033[0;37;41mCOLOR1\033[1;35;44mCOLOR2\033[m"

This should output testing COLOR1COLOR2 with COLOR1 being white (light gray) text on a red background, and COLOR2 being bold / bright magenta text on a blue background. All subsequent text should be normally colored.

Try playing around with that echo command until you feel a bit more comfortable.

Reference Table

Obviously you don't want to come back to this page every time you want to change your Bash colors. Here is the shell script (origin) used to generate the lovely color table:

#!/bin/bash
#
# This file echoes a bunch of color codes to the terminal to demonstrate
# what's available. Each line is the color code of one forground color,
# out of 17 (default + 16 escapes), followed by a test use of that color
# on all nine background colors (default + 8 escapes).
#
T='gYw'   # The test text
echo -e "\n                 40m     41m     42m     43m     44m     45m     46m     47m";
for FGs in '    m' '   1m' '  30m' '1;30m' '  31m' '1;31m' '  32m' '1;32m' '  33m' '1;33m' '  34m' '1;34m' '  35m' '1;35m' '  36m' '1;36m' '  37m' '1;37m';
  do FG=${FGs// /}
  echo -en " $FGs \033[$FG  $T  "
  for BG in 40m 41m 42m 43m 44m 45m 46m 47m;
    do echo -en "$EINS \033[$FG\033[$BG  $T \033[0m\033[$BG \033[0m";
  done
  echo;
done
echo

Finally, the Goal

This is the PS1 which generates the slightly steam-punk looking Bash prompt shown at the start:

c="\[\033["
n="${c}m\]"
PS1="$n\n╔ǁ "\
"${c}1;3\$(if [ \$? -eq 0 ]; then echo '2'; else echo '1'; fi)m\]*$n ǁ "\
"${c}36m\]\@ \d$n ǁ "\
"${c}35m\]\j$n ǁ "\
"${c}30m\]\u"\
"${c}33m\]@\h$n ǁ "\
"${c}1;34m\]\w$n ǁ "\
"${c}32m\]\$(ls -1 | wc -l | tr -d ' ') files$n ǁ═╗\n╚═"\
"${c}1;3\$(if [ ${EUID} -eq 0 ]; then echo '1'; else echo '4'; fi)m\]»$n "

Hopefully, at this point you are reading these lines of seeming gibberish like plain English. If not, play more. You'll get it.

256 (8-bit) Colors

It is also possible (in these newfangled modern times) to use a full 256 colors in most consoles. The following color table can be used to determine the code for each color by adding the column and row number. In order to make use of these codes, we use the syntax 33[38;5;#m for the foreground (text) and 33[48;5;#m for the background, or in a single statement like 33[38;5;#;48;5;#m to set both at once, where # is an 8-bit (0-255) color code.

   +   0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

   0                                                 

  16                                                                                                             

  52                                                                                                             

  88                                                                                                             

 124                                                                                                             

 160                                                                                                             

 196                                                                                                             

 232                                                                                                             

The best way to understand is to try it out.

echo -e "testing \033[38;5;196;48;5;21mCOLOR1\033[38;5;208;48;5;159mCOLOR2\033[m"

Reference Table

Once again, you most likely don't want to come back here just to look up a 256 color code. This is my own code. It's shared under CC BY.

#!/bin/bash
#
# generates an 8 bit color table (256 colors) for
# reference purposes, using the \033[48;5;${val}m
# ANSI CSI+SGR (see "ANSI Code" on Wikipedia)
#
echo -en "\n   +  "

for i in {0..35}; do
  printf "%2b " $i
done

printf "\n\n %3b  " 0
for i in {0..15}; do
  echo -en "\033[48;5;${i}m  \033[m "
done

#for i in 16 52 88 124 160 196 232; do
for i in {0..6}; do
  let "i = i*36 +16"
  printf "\n\n %3b  " $i
  for j in {0..35}; do
    let "val = i+j"
    echo -en "\033[48;5;${val}m  \033[m "
  done
done

echo -e "\n"

See Also

share this post