I like to pack some extra contextual information into my bash prompt, mostly through the use of color.
The creaky old UNIX machines I first learned on offered this as the default shell prompt:
Today, the situation is considerably improved; here is the default bash prompt in recent versions of Ubuntu:
This prompt is more helpful because it reminds you of important facts about your environment.
But there is still a lot of room for improvement. Starting from the modern standard prompt, I’ve gradually embellished it to reflect more contextual information. In most cases, though, I have opted to display this information through color rather than adding text – the prompt is already a bit wide for comfort when I am working with a path like
Components of the prompt
A typical prompt for me looks like this:
To break that down a bit:
It’s very important to know who you are. While the text of the name is certainly sufficient for figuring out which user you are, it’s easy to accidentally ignore it after a while. To combat that, I use color to indicate special conditions.
I’m me. This is of course the most common case, so for that I draw the username in green.
roothas the most potential for accidental destruction, it calls for red alert.
It’s also important to know where you are, so I conditionally highlight my hostnames. Before I did this, I would embarassingly often fail to notice that a given terminal tab was actually connected to a remote shell, which lead to some unnecessary pain.
I have no principled scheme for assigning colors – I simply have a case block that maps colors to particular hosts I deal with. However, I am more likely to use muted tones on personal machines and more striking colors on important production servers. There is also a default color for servers not explicitly mentioned, which reminds me that I’m connected to a VM or a server I don’t log into very often.
The other half of knowing where you are is your current working path. I cannot fathom any shell prompt that does not include the working directory. Even DOS had this.
My use of color here presently distinguishes several cases for the working path:
Path is a symbolic link. I follow the convention of
ls --colorand show these in cyan.
Path is private. That is, the permission bits are
rwx------and no other user has access. I’ve got this case set to magenta, which is fairly arbitrary. I think of it as “purple for private”.
Path is world-writable. This directory has the most liberal permissions possible,
rwxrwxrwx. I don’t follow the default
lsconvention for this, because blue text on a green background makes my eyes bleed. For lack of a better idea, I show this case in yellow.
Path is not writable to me. I leave these in grey, and my mnemonic for this is that it’s greyed out like a disabled form control.
Path is otherwise nothing special. That is, it’s a path for which none of the above appiles. This is the case for most paths in my
$HOMEand below. I default to showing the path in blue, much like
I recently altered the code to apply this colorization is independently for each directory along the current working path. It’s alreay come in handy, as I’ve been able to “smell” some permissions issues and symlinks I might not have noticed otherwise.
If you use git a lot, this one is extemely useful. I have more than once stumbled into a directory and found it to be a git repository by surprise – I would rarely think to preemptively query to see if a given dir is a git repo.
Beyond that, it’s amazingly easy to forget that you’ve been working in a different git branch, and having it printed between every command pretty much eliminates that problem.
It works by calling a bash function
__git_ps1, which prepares a string of the form:
Where the single-character flags have the following meanings:
||the working copy is dirty|
||changes have been staged in the index|
||there are stashed changes|
||there are untracked files|
||local repo is ahead of origin|
||local repo is at par with origin|
||local repo is behind origin|
The untracked files indicator can come in handy when running a build script – if that
% suddenly shows up, you probably have some
.gitignore additions to make.
If the current working path is not in a git repository, this section does not appear.
Unfortunately, the last time I tried this on Cygwin, it was too slow to use. Launching a processes on Windows is heavier operation than on Linux, but I’m not sure if that could account for the dramatic speed difference. If my situation calls for using Windows, I just have to live without the git stuff.
It’s common to terminate the prompt with a symbol of some kind. Traditionally, the dollar sign (
$) is used for regular users, and a octothorpe (
#) is used for root. I deviate from tradition slightly by using
∫, a unicode glyph representing Leibniz’ integration symbol.
$, it is also a form of the letter ‘S’, but it’s a bit less overloaded with shell-related meanings. It’s also a fairly tall glyph, so it helps separate the prompt from shell commands.
The unicode math symbols, technical symbols, and miscellaneous symbols are pretty good places to start looking for interesting decorations.
Another prompt decoration deviant, Jorge Israel Peña, uses a lambda glyph and an arrow to make his prompt resemble a Haskell lambda expression, which is a pretty cool idea.
λ ~/code/haskell ➜
While I don’t find myself using chroots very often these days, I still leave in a bit of chroot-indicating code I inherited from some ancient version of Debian. This prefix is only shown when I’m actually in a chroot.
There are some other possibilities I’ve considered including.
I have experimented with including the current time in the prompt, which seems like it would be handy for logging purposes. But it adds a lot of width, and my prompt is already pretty verbose.
A more concise alternative might simply be to add a command sequence number that increments with each command. There are escape sequences for that (among other things) in the Bash Prompt HOWTO.
Another interesting idea would be a (conditionally colorized) exit status from the previous command.
Some people use a multi-line prompt, with the long part on a separate line from the terminator. This was the default prompt setup in Cygwin when I started (and may still be, for all I know). After trying both ways, I prefer the single-line setup.
If I used a lot of other VCS systems, I would include status code for those. But these days I really only use git and Subversion, and all of my svn use is via git-svn, so I haven’t felt the need.
Appendix: Implementation Details
I use bash, so the specific implementation instructions below are for bash.
Defining the Prompt
The Primary Prompt Variable
The primary bash prompt is controlled by a shell variable called
$PS1. You can fill it with both literal text and special sequences that get replaced by contextual information, such as the name of the current user and current working directory. The
$PS1 string for the default
user@host:~/path$ prompt I mentioned above is simply
'\u@\h:\w\$ '. There are many more special sequences listed in the bash man page.
$PS1 to a static string is sufficient for many prompts. Bash can supply a lot of context-sensitivty through substitution of escapes like
\w. But to achieve my fancy per-path-segment highlighting described above, I resorted to rebuilding
$PS1 dynamically between each command.
Before drawing the prompt, bash will evaluate any code stored in the
$PROMPT_COMMAND variable. Mine looks like this:
build_prompt is a bash function that does all the work of calculating the
Color is achieved by emitting special ANSI terminal sequences that alter the foreground and background color of text. My prompt script simply defines some useful colors as environment variables, which I can use simply by printing their value. I obtain the sequences from a tool called
tput, which knows the right sequences for various terminal types.
RED=$(tput setaf 1) GREEN=$(tput setaf 2) YELLOW=$(tput setaf 3) # etc.
Bash is not itself smart enough to know about these ANSI sequences, so we have to use special prompt-specific escape sequences,
\], to mark the boundaries of non-printing strings. If you leave these out, bash will miscalculate the width of the prompt, and this can cause nasty drawing problems.
This requires some bash scripting, usually in the form of case blocks. A typical one for me looks like this:
case "$USER" in nathan|nstien|npstien|Nathan|"Nathan Stien") usercolor=$GREEN terminator=∫ ;; root|Administrator) usercolor=$BOLD$RED; terminator='#' ;; *) usercolor=$BOLD$YELLOW terminator='$' ;; esac
The general pattern is that I inspect various environment variables (including
$PWD) and set some new variables to contain the desired colors. Those variables are then used in the prompt definition.
Putting It All Together
I’ve tried to outline some of the general techniques behind my specific prompt, but I think the best way to get the full picture is to go look at the code. My prompt definition is in a separate file
.bash_prompt that I source from my main