Tuesday, January 14, 2014

Coloring a bash command prompt

Bash, the Bourne Again SHell, is the default command shell used for most Linux distributions and MacOS.  One of the nice things about it (and most Unix shells, for that matter) is the flexibility you have in configuring the command prompt.  When using a color-capable terminal program, I like to make the command prompt a slightly different color than the rest of the text in that window, just so it stands out when I skim backward through miles of output.  There's a couple different methods for doing that, and not all of them work well.  Here's the best method I've found.

The prompt string in bash is determined by the $PS1 environment variable.  There are lots of different backslash-escaped codes that can be placed in that variable to make it look any way you like, but I won't go into those here.  Read this web page for more info on that.  The $PS2 environment variable determines the prompt given for continued lines, such as when you type a "while" loop.  The default PS1 is "\u@\h:\w\$ ", which gives the format "user@host:directory$ ".  The default PS2 is simply "> ".  The PS1 variable can be set in your ~/.bashrc file, if it's not already there, using a command like this:

export PS1="\u@\h:\w\$ "

You can run that same command from the command prompt to play with these settings interactively before you modify your .bashrc.

The general method for coloring text is to issue an escape code to the terminal program which tells it which of the handful of standard colors or typefaces should be used.  By inserting this escape code into your PS1 variable, you can change the terminal's font color when it's drawing your prompt on the screen.  I'll show you the old school method first just for completeness, but be warned that this method gives screwy line wrapping for long command lines in bash, and should not be used.

Deprecated method:

The color-setting escape code is "\e[0;3Xm", where "X" is a 1-digit number representing the color to be used.  The code to return the color to normal is "\e[m".  The 0 before the semicolon means to use a normal-weighted font in the given color.  Other values for that digit are:

0:  normal
1:  boldface, or perhaps a lighter version of that color, depending on your terminal and font choice
4:  underlined
5:  blinking (gah!)
7:  inverted colors

You can also add another ";4Y" before the final "m" in that escape code to control the background color.  See the image below for several examples.  As a simple example, the following command will keep the default command prompt format, but will color it magenta:

export PS1="\e[0;35m\u@\h:\w\$ \e[m"

To do the same thing in a csh-style shell like tcsh, do this:

set prompt="%{\e[0;35m%}%n@%m:%/\$ %{\e[0m%}"

The color codes for foreground colors begin with 3, while the background colors begin with 4.  The available colors and their 1-digit codes follow.  The "bold" color is in parentheses, if it's different.

0:  black (grey)
1:  red
2:  green
3:  brown (yellow)
4:  blue
5:  purple (magenta)
6:  cyan
7:  white

I started out using this method, but quickly ditched it in bash because the escape codes played havoc with the line wrapping when I'd type really long commands.  Fortunately, csh variants work just fine with this method, so I've continued using it for that shell.

A few example color schemes and the command that created each

Preferred method:

The better method for bash is to use the program "tput" to generate the appropriate code for whatever terminal setting your terminal program uses.  This generally makes easier-to-read commands.  The 1-digit color codes are the same as above.  The "tput" command takes one or two arguments:  the value to set, and the color code to use.  The following tput commands are available:

tput bold (sets bold typeface)
tput rev (inverts the current color scheme)
tput sgr0 (resets everything to system defaults)
tput setaf X (sets the foreground color to X)
tput setab X (sets the background color to X)

The following commands create the same command prompt as above, but without any problems wrapping long command lines.  Pick & choose the color you want to use in PS1.

BLACK=$(tput setaf 0)
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
BROWN=$(tput setaf 3)
BLUE=$(tput setaf 4)
MAGENTA=$(tput setaf 5)
CYAN=$(tput setaf 6)
WHITE=$(tput setaf 7)
STOP=$(tput sgr0)
export PS1="\[$MAGENTA\]\u@\h:\w\$ \[$STOP\]"
export PS2="\[$MAGENTA\]> \[$STOP\]"

If you want to get really gaudy, you can mix multiple colors inside a single prompt, like this:

export PS1="\[$RED\]\u\[$GREEN\]@\h:\[$BLUE\]\w\$ \[$STOP\]"

Just make sure you finish it up with the STOP command, or all the rest of the text in your window will get printed in the last color you set.

I use this second method in my .bashrc file at work, which is shared among multiple machines.  I use a different color prompt on each machine, just so I don't get confused about where I'm typing.  The following code does this for me:

# Color the command prompt differently on each host.
if [ `hostname` = "grumpy" ]; then
    COLOR=$(tput setaf 1)
elif [ `hostname` = "sneezy" ]; then
    COLOR=$(tput setaf 2)
elif [ `hostname` = "doc" ]; then
    COLOR=$(tput setaf 3)
fi
STOP=$(tput sgr0)
export PS1="\[$COLOR\]\u@\h:\w\$ \[$STOP\]"
export PS2="\[$COLOR\]> \[$STOP\]"


In the above example, you can see how much easier it is to distinguish between the output of two adjacent commands when the command prompt itself stands out like it does.  So that's what I learned recently about setting font colors in Unix shells.  If you've got any other suggestions for useful ways to customize your desktop environments, please share them in the comments below.

No comments:

Post a Comment

Please leave your comment below. Comments are moderated, so don't be alarmed if your note doesn't appear immediately. Also, please don't use my blog to advertise your own web site unless it's related to the discussion at hand.