Using ANSI Color Codes to Colorize Your Bash Prompt on Linux
╔╣ * ║ 08:24 AM Fri Aug 17 ║ 0 ║ michaelplotke@work ║ ~/Desktop ║ 11 files ╠═╗ ╚═»
The goal is to create a design similar to the above example, but with more artistic merit. To accomplish this, we must understand several things, including: special characters, some bash scripting, and ANSI colors.
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.
Basic 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 and some plain text.
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"