Adding Dynamic Coloring of Last Return Value to Your Bash Shell Prompt
Some time ago I came across a posting which outlined a Bash prompt containing the return value of the previous command, colored green if the command exited normally ($? == 0) and red if the command exited with an error ($? != 0).
I wish I could remember where I found it; unfortunately, after some extensive Googling I’ve been unable to locate my original source. At any rate, it was broken when I found it, so I had to fix it, and I finally figured out how yesterday.
The original looked something like this:
PS1="[\u@\h \W] \`if [[ \$? = '0' ]]; then echo -ne '\e[32m${?}\e[0m'; else echo -ne '\e[31m${?}\e[0m'; fi\` \$ "
Unfortunately, the author must not have tested that code, because all it produced was:
[morganiq@benvolio morganiq] 0 $ blah
-bash: blah: command not found
[morganiq@benvolio morganiq] 0 $
After some amount of tweaking, I isolated three issues:
- $? was actually being updated in the middle of the script — after each command between the backticks was executed — so I needed to assign $? to a variable at the beginning if it was going to work.
- echo -e was preventing me from inserting $RETVAL, because it needed to be escaped but between the single quotes it couldn’t be, so I’d need to split up the echo’s as well.
- The color escape sequences were somehow incomplete, and caused issues if the command line wrapped. After living with the buggy wrapping for some time, I finally decided it wasn’t normal and that there was probably a more correct way to do it.
The end result was:
PS1="[\u@\h \W] \`RETVAL=\$?; if [ \$RETVAL -eq 0 ]; then echo -n '\[\033[0;32m\]'; echo -n \$RETVAL; echo -n '\[\033[0m\]'; else echo -n '\[\033[0;31m\]'; echo -n \$RETVAL; echo -n '\[\033[0m\]'; fi\` \$ "
Which outputs exactly what it’s supposed to:
[morganiq@benvolio morganiq] 0 $ blah
-bash: blah: command not found
[morganiq@benvolio morganiq] 127 $
Some notes on the corrections and the :
- \033 is the octal equivalent of \e; both are expanded to the ASCII character ESC (decimal 27).
- The -e switch to echo probably became unnecessary when I removed the ${?}, which never worked right to begin with.
- The backticks (`), as well as all variables within them, are escaped to delay interpolation of the code until each time the prompt is displayed, rather than only once, at login.
For the uninitiated, this line belongs in /etc/bashrc to make it system-wide (apply to all users when they use the shell), or ~/.bashrc in your home directory to make it only apply to you. If you go for /etc/bashrc, as I would recommend provided that no other geeks use your machine, you’ll need to:
- Edit the file as root (I like using pico as an editor, so I’d use sudo pico /etc/bashrc, for instance, or su - then pico /etc/bashrc)
- Find where the PS1 variable is assigned and comment that line out (by adding a # to the beginning of the line), as well as any PS2’s or higher if present (highly unlikely)
- Paste in the new code on a new line
- Save
- Log out & log back in, or su - yourusername in another terminal
- If it doesn’t work as advertised, or if you don’t like it, just go back and remove the new line (or comment it out), and uncomment the original line and repeat steps 4 & 5.
For more information about Bash and shell prompts, here are a couple of good references:
- Advanced Bash-Scripting Guide
- The most comprehensive, well-written public guide to bash scripting that I’ve come across.
- Bash Prompt HOWTO
- Focuses on customizing your Bash prompt.
- Giles Orr’s Bash Prompts gallery
- A gallery of Bash prompts, some of which are incredibly complex.
- Asciitable.com
- The ASCII tables, complete with decimal, hexadecimal and octal codes, as well as HTML character entity names where applicable.
- Will Guraldi’s ANSI escape codes reference
- A good compilation of all of the ANSI escape sequences.