Bash prompts: the essential

Posted by – 05/11/2008

Bash is probably the most common command-line shell in the GNU/Linux world. Although a lot of people use alternate shells (such as Zsh), Bash is still shipped with most mainstream distros as the default. Once you have a lot of different remote machines, all running Bash as the shell, it becomes increasingly difficult to pay attention to the prompt, and typing reboot in a machine different from the one you wanted becomes more likely. I deal with that problem by changing Bash prompts…

First of all, the basics: Bash prompts are just environment variables with special characters you can set and export. Bash has four of these variables: PS1 to PS4, but usually only the first two matters (actually, just PS1 – for a reference on the others, check the manpage). The most common PS1 string is:

spectra@home:~$ echo $PS1
\u@\h:\w\$
spectra@home:~$

This has 4 special characters, escaped with a backslash: \u informing us the username; \h informing us the hostname; \w, informing us the working directory; and \$, which gives us the $ in the end of the prompt (more on this later).

So, essentially, one can change that string to anything else…

spectra@home:~$ PS1="my_shell_prompt\$ "
my_shell_prompt$

Pretty easy. You can check a complete reference of the special characters at the section PROMPTING of bash manpage, but the most useful IMHO are the following:

  • \d the date
  • \t the time (24-hour format)
  • \W the basename of the current working directory
  • \! the history number of this command
  • \# the command number of this command
  • \$ shows # if the UID is 0 (is we are root), or $ for all the rest

Also, as part of the prompt string, one can use ANSI Colors enclosed as non-printing characters (that is between \[ and \]). ANSI sequences always begin with an “ESC[” and end with an “m”. (Yes… Really arbitrary… but that’s the way it is…). ESC can be represented as \e… Here is a list of the most common colors in ANSI sequences:

  • Black: 0;30
  • Red: 0;31
  • Green: 0;32
  • Brown: 0;33
  • Blue: 0;34
  • Purple: 0;35
  • Cyan: 0;36
  • Light Gray: 0;37

Now, notice that there are two numbers separated by a semi-colon… the first is always 0 (zero) in the colors I pointed above, but it actually refers to an ANSI attribute called Select Graphic Rendition… You can use 0 (zero) to normal colors, 1 for bold, 2 to faint, etc. So \e[0;30m refers to BLACK, \e[1;30m refers to DARK GREY. The Wikipedia has a good article on these escape sequences.

Once you’re satisfied with something printed in a color, to go back to the default (to reset), you issue the \e[0m escape sequence.

So, back to my problem… Each different machine gets a different color for the hostname. On “hospital” machine, for instance, my PS1 looks like:

spectra@hospital:~$ PS1="\[\e[1;33m\]\u\[\e[0m\]@\[\e[0;35m\]\h\[\e[0m\]:\[\e[0;32m\]\w\[\e[0m\]\$ "
spectra@hospital:~$

With \e[0;35m (Purple) for the hostname. On “home” machine, it may be \e[0;34m (Blue)… On “server”, it may be \e[0;36m (Cyan), and so on… After a while, you get used to the color and end up linking the color to the machine… so that typing “reboot” on a machine with the wrong color gets harder than before.

To make the changes permanent, put export PS1 in one of the config script of bash (.bashrc, .bash_profile, etc). On some systems, /etc/environment holds lots of environment variables definitions.

I just scratched the surface… That’s just what works for me… The Bash-Prompt-HOWTO has some interesting examples, and I actually have a friend who uses more esoterical stuff, such as fancybash or bashish, but I’ll leave this up to you…

9 Comments on Bash prompts: the essential

  1. spectra says:

    @Dummy0001,

    As I said, that’s just what works for me… maybe I have some rather good color recognization stream of thought 🙂

    As for limiting the time spent as root… sure, that’s a good advice.

  2. spectra says:

    @Daniel,

    I also expected it to be that way… 🙂 For now I just changed to italics, but I definitively have to fix that.

  3. Dummy00001 says:

    That’s rather useless, I’d say.

    Best protection against typing “reboot” on wrong system?

    Limit the time you work as root. Plain and simple. And it actually helps in a lot of other situations.

    P.S. As to shell prompt coloring, Here is my PS1 made after idea picked on Slashdot.

    PS1=’\T [\h:\w]\[`test ”$?” -ne “0” && echo -ne ”\e[31m”`\]\$\[`echo -ne ”\e[0m”`\] ‘

    The $/# would change color to red if last command return code wasn’t 0.

  4. Hi Pablo,

    A good tool to avoid rebooting the wrong server is Martin Krafft’s excellent package: http://packages.debian.org/sid/molly-guard

    It hooks into the reboot/shutdow commands and asks you to type the hostname of the box you want to reboot (if it’s not the local machine).

    Francois

  5. Gunnar says:

    For Benjamin: Well, this is surely not as secure as a hash, but given you only have eight colors (well, if you like intensity and stuff, you can add some more)… But this rough thingy would work:

    NUMCOLORS=8; HOSTCOLOR=$(( $( ip a|grep ‘inet ’|grep -v 127.0.0.1|awk ‘{print $2}’|sed ’s/[^0123456789]//g’ |head -1) % $NUMCOLORS ))

    Of course, with eight colors you won’t achieve much, and this is a way too simple function (i.e. the highest bits of the IP won’t even work, you can get black/black, etc). Of course, this can serve as a first approach – then you can get, say, the BGCOLOR by reversing the IP, and have a bit more combinatory space – And don’t forget to include the hostname string! 😉
    Bah, it’s just easier, cleaner and better (although it still looks like crap!) to do:

    HOSTCOLOR=$(( $(echo “0x$(hostname | md5sum |cut -f 1 -d \ )”) % $NUMCOLORS))

    Anyway, there you go, easy automatic (and probably autougly) host color choosing!

  6. Good article, Pablo. I have had similar problems in the past, e.g. shutting down the wrong machine, and came to the same conclusion.

    I have a script called bashprompt that I have evolved, which is placed in /etc and handles my prompts. The nice thing about it is that you only need to place an echo ”. /etc/bashprompt” in each users’ .bashrc (and /etc/skel/.bashrc)

  7. Whoops! i expected code elements to be inline-styled, and pre elements to be block-styled, and there was no preview option. Sorry about the poorly-formatted comment above! Feel free to edit the code elements to tt (or whatever makes it readable).

  8. Including the literal string $? in $PS1 allows you to see the return code of your previous process. I find that this encourages me to think actively about the state of the processes i run, which in turn is really useful as i translate my everyday shell use into automated scripts.

    If you don’t have it set in $PS1 yet, but you want to try it out temporarily, just do:

    PS1='$? '$PS1

    That will prefix your prompt with the return code of the most recent command. (Make sure you use single-quotes around it to protect $? from getting interpreted by the shell during the variable assignment itself, instead of at prompt generation time.)

  9. Benjamin Seidenberg says:

    I really would like to have a bashrc where the hostname is displayed in a color based on a hash of itself, a la most IRC clients. This could be used in a bash_profile that’s shared across a network for places that do NFS homes, etc.

Leave a Reply

Your email address will not be published. Required fields are marked *