Chapter 6. Colours and Cursor Movement

6.1. Colours

As mentioned before, non-printing escape sequences have to be enclosed in \[ and \] to prevent Bash from counting non-printing characters towards the length of the prompt. Changes in colour in the prompt are definitely non-printing sequences.

If you try out the following prompts in an xterm and find that you aren't seeing the colours named, check out your ~/.Xdefaults file (and possibly its brethren) for lines like XTerm*Foreground: BlanchedAlmond. This can be commented out by placing an exclamation mark ("!") in front of it. Of course, this will also be dependent on what terminal emulator you're using. This is the likeliest place that your term foreground colours would be overridden.

To include blue text in the prompt:

PS1="\[\033[34m\][\D{%H%M}][\u@\h:\w]$ "

This prompt begins with \[ denoting a non-printing sequence, then \033[ indicating an ANSI escape sequence, then 34m which is the actual colour code, and finally an indication that the non-printing sequence is ending \]. The rest is the visible part of the prompt.

The problem with this prompt is that the blue colour that starts with the 34 colour code is never switched back to the regular colour, so any text you type after the prompt is still in the colour of the prompt. This is also a dark shade of blue, so combining it with the bold code might help:

PS1="\[\033[1;34m\][\D{%H%M}][\u@\h:\w]$\[\033[0m\] "

The addition of 1; gives us bold. The prompt is now in light blue, and it ends by switching the colour back to the default, indicated by the code 0m.

Here are the rest of the colour equivalences:

Black       0;30     Dark Gray     1;30
Blue        0;34     Light Blue    1;34
Green       0;32     Light Green   1;32
Cyan        0;36     Light Cyan    1;36
Red         0;31     Light Red     1;31
Purple      0;35     Light Purple  1;35
Brown       0;33     Yellow        1;33
Light Gray  0;37     White         1;37

You can also set background colours by using 44 for Blue background, 41 for a Red background, etc. There are no bold background colours. Combinations can be used, like Light Red text on a Blue background: \[\033[44;1;31m\], although setting the colours separately seems to work better (ie. \[\033[44m\]\[\033[1;31m\]). Other codes available include 4: Underscore, 5: Blink, 7: Inverse, and 8: Concealed.

Note

Many people (myself included) object strongly to the "blink" attribute because it's extremely distracting and irritating. Fortunately, it doesn't work in any terminal emulators that I'm aware of - but it will still work on the console.

Note

If you were wondering (as I did) "What use is a 'Concealed' attribute?!" - I saw it used in an example shell script (not a prompt) to allow someone to type in a password without it being echoed to the screen. However, this attribute doesn't seem to be honoured by many terms other than "Xterm."

Colours can also be set using the tput command. tput is considered more "portable," but I generally prefer ANSI colours because it avoids spawning an external program, and I've never seen a problem using ANSI colours (then again, I use only AMD/Intel-based PCs running Linux - I don't use Bash on a Mac or an old Sparcstation where this might be more of an issue). This portability becomes important when we discuss cursor movement in the next section: many new terminal programs don't support the full range of ANSI cursor movements.

Based on a prompt called "elite2" in the Bashprompt package (which I've modified to work better on a standard console, rather than with the special xterm fonts required to view the original properly), this is a prompt I've used a lot:

 
function elit
{

local GRAY="\[\033[1;30m\]"
local CYAN="\[\033[0;36m\]"
local LIGHT_CYAN="\[\033[1;36m\]"
local NO_COLOUR="\[\033[0m\]"

case $TERM in
    xterm*|rxvt*)
        local TITLEBAR='\[\033]0;\u@\h:\w\007\]'
        ;;
    *)
        local TITLEBAR=""
        ;;
esac

local temp=$(tty)
local TTY=${temp:5}
PS1="${TITLEBAR}\
${GRAY}-${CYAN}-${LIGHT_CYAN}(\
${CYAN}\u${GRAY}@${CYAN}\h\
${LIGHT_CYAN})${CYAN}-${LIGHT_CYAN}(\
${CYAN}\#${GRAY}/${CYAN}${TTY}\
${LIGHT_CYAN})${CYAN}-${LIGHT_CYAN}(\
${CYAN}\D{%H%M}${GRAY}/${CYAN}\D{%d-%b-%y}\
${LIGHT_CYAN})${CYAN}-${GRAY}-\
\n\
${GRAY}-${CYAN}-${LIGHT_CYAN}(\
${CYAN}\$${GRAY}:${CYAN}\w\
${LIGHT_CYAN})${CYAN}-${GRAY}-${NO_COLOUR} " 
PS2="${LIGHT_CYAN}-${CYAN}-${GRAY}-${NO_COLOUR} "
}

I define the colours as temporary shell variables in the name of readability. It's easier to work with. The "TTY" variable is a check to determine what terminal you're on. Like the test to determine if you're working in an Xterm (case $TERM in), it only needs to be done once. The prompt you see look like this, except in colour:

The "elit" prompt listed earlier.

To help myself remember what colours are available, I wrote a script that output all the colours to the screen. Daniel Crisman has supplied a much nicer version which I include below:

#!/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 '   0m' ' 1;0m' '  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";
  done
  echo;
done
echo

The output of the colours script.

The same script implemented with tput:

#!/bin/bash

T='gYw'   # The test text

echo "Background:      0       1       2       3       4       5       6       7"
echo "Foreground:"

for FG in 0 1 2 3 4 5 6 7
do
  echo -en " ${FG}     $(tput setaf ${FG})  $T  "
  for BG in 0 1 2 3 4 5 6 7
  do 
    echo -en "$(tput setaf ${FG})$(tput setab ${BG})  $T  ";
    echo -en "$(tput sgr0) "
  done
  echo
  #  Do it again, in bold:
  echo -en " ${FG}bold $(tput bold)$(tput setaf ${FG})  $T  "
  for BG in 0 1 2 3 4 5 6 7
  do 
    echo -en "$(tput bold)$(tput setaf ${FG})$(tput setab ${BG})  $T  ";
    echo -en "$(tput sgr0) "
  done
  echo
done
echo