Customizing Your Bash Command Prompt

September 21, 2011 by . 7 comments

When you’re working on your Linux or Mac OS X system’s command line, the prompt is the text to the left of the commands you enter. The default prompt varies for every system, but it usually gives you an indication of your username, your machine’s host name and your current working directory. Also, it ends with a dollar sign $ if you’re working as a normal user. If you’re working with root privileges, it ends with # instead.

The prompt can be customized to include relevant information that can help you increase productivity, to hide information you don’t care about, or to highlight the lines in your terminal output where you entered commands.

This post will show how to customize your bash prompt, and, in the process, explain a few of its more advanced features that improve your productivity. Bash is the default shell on Mac OS X, available for all (or most) Linux distributions, if not already included, and available on Windows via Cygwin.

Bash primarily uses two variables that define how your prompt looks like:

  • PS1 specifies the format of your regular prompt that appears to the left of every new command you type
  • PS2 defines the continuation prompt: It appears when you enter a line ending with a backslash to continue input on the next line.

In the following example, I split the ls command over two input lines to show my system’s default PS1 and PS2 in action:

Saurus:~ danielbeck$ l
> s
Desktop Documents Downloads Library Movies Music Pictures Public Sites
Saurus:~ danielbeck$ _
In this post’s monospaced example sections, an underscore will represent the cursor position when showing examples of a prompt, while a single $ character represents a generic prompt, except where otherwise noted.

As you can see, my PS1 prompt contains

  • the host name (Saurus),
  • the current working directory (~ is a synonym to the user’s home directory),
  • my username (danielbeck),
  • and an indicator that I am a regular user ($).

The second line is my default continuation prompt: A greater-than sign, followed by a single space.

You can assign different values to your prompt right in your shell session, as shown below. The values you set this way will be reset once you close your session, so you can experiment freely with the examples in this post. Just restart your shell session to reset the prompt variables to your permanent default values.

Saurus:~ danielbeck$ PS1='my new prompt $ '
my new prompt $ _ 

To display the currently assigned values (e.g. for copying them to your bash configuration files), you canecho these variables. The second line in the following example is the output of the command on the first line, the prompt is repeated after execution on the third line:

my new prompt $ echo $PS1
my new prompt $ 
my new prompt $ _ 

Once you start adding shell scripts and program invocations to your prompt (see Executing scripts and programs below), this will already contain evaluated expressions, so you should not rely on this alone.

Displaying additional information

Bash supports several placeholder sequences for certain kinds of information that are commonly displayed in a prompt. These take the format of a backslash , followed usually by a single character. Of course, you can find a list of all possible placeholders in the bash manual. To re-create the default prompt shown above, one would enter the following:

 $ PS1='\h:\W \u\$' 

This prompt contains the host name, followed by a colon, followed by the current working directory, a space character, the user name, and the prompt indicator. As already mentioned above, it is a hash character # if the user has root privileges, and a dollar character $ otherwise.

In this question, Xenorose asked:

I’m currently on a Linux machine and the shell prompt is showing me the […] number of executed commands. My own computer doesn’t have this, how can I configure it?

The answer is the placeholder sequence \#: It prints the command number of the command following it. The first command of a bash session starts at 1, and every subsequent command increases it by 1. In the following example, PS1='\# \$ ':

Last login: Thu Aug 25 21:54:14 on ttys001
1 $ pwd
/Users/danielbeck
2 $ pwd
/Users/danielbeck
3 $ pwd
/Users/danielbeck
4 $ _ 

A very similar sequence is ! which represents the history number of the command following it. You can execute the command on that line again by entering an exclamation mark, followed by that command’s history number.

$ PS1='\! \$ ' 
1478 $ pwd
/Users/danielbeck 
1479 $ cd / 
1480 $ !1478
pwd
/
1481 $ _ 

As you can see, the command executed through history expansion of !1478 is printed again (pwd), and then executed, this time with different results.

Once your prompt grows in length, you might want to add a line break or two to it by adding n at the appropriate positions.

Colors

The prompt, or parts of it, can also be a different color than the default. Doing this is a bit tricky though. There are another few special sequences used for this. Similar to HTML or other document markup languages, the highlighted parts are enclosed in a pair of special formatting sequences. For HTML, this would be the and tag pair. In bash, it looks like this:

regular text \[\033[0;32m\] green text \[\033[m\] regular text again 
This example produces the following output:

Screenshot of colored prompt

The actual formatting code is enclosed, similar to angle brackets for HTML tags. \[ and \] are special sequences that indicate non-printing sequences, so bash can ignore everything between them for length calculations (e.g. when wrapping output lines depending on the terminal width). Strictly speaking, they’re optional, but you don’t want to mess up your terminal’s line breaking behavior by skipping them.

\033 is another special sequence: The digits are octal and represent the ASCII character with the corresponding code, in this case Escape. It starts a terminal control escape sequence (\033[…m) specifying font and color attributes. For a list of such escape sequences, see this web page.

We use two of these sequences:

  • \033[0;32m resets text formatting and makes the following output a regular green
  • \033[m is an "empty" sequence containing no formatting rules. It resets output formatting to its defaults.

This page provides a nice PS1 (at the bottom) so you can see all colors and other formatting options as part of a single PS1. Just be prepared to endure blinking text, if your terminal emulator can handle it.

Through some bash debugging facilities, it's even possible to add these formatting rules to the commands you enter.

Executing scripts and programs

You can execute arbitrary CLI programs as part of displaying your prompt, adding their output to it. For example, you might not like the behavior of \W to print ~ instead of the full path to your home directory. No problem, just run pwd, enclosed in $( ), instead:

Saurus:~ danielbeck$ PS1='\h:$( pwd ) \u\$ '
Saurus:/Users/danielbeck danielbeck$ _ 

We usually have a choice of enclosing strings in single or double quotation marks. Since bash evaluates all expressions enclosed in $(…), like $( pwd ) here, as well as variables (e.g. $PS1), when enclosed in double quotation marks, we need to make sure to put values assigned to PS1 in single quotation marks now. It didn't make a difference earlier, but we want these commands to be evaluated every time PS1 is printed by the shell, not just once when the value is assigned to PS1. Just remember to always use single quotation marks and you won't run into this problem.


In my experience, it's sometimes easy to overlook non-zero return codes of more complex commands with many lines of output. To make sure I notice these possible errors, I can add the previous command's return code to my prompt, like with the following PS1:

PS1='$( RET=$?; if [ $RET != 0 ] ; then echo "rc: $RET"; fi )\n\$ ' 

As a side effect, your prompt will now always start with a blank line if the previous command returned 0. While you can add line breaks to your prompt by using the sequence n, bash discards all other newlines that are a result of e.g. multi-line program return values.

If you want to have a multi-line output depending on some conditions, you need to put that part of your prompt into the variable PROMPT_COMMAND. It is expected to contain a script that is executed just before PS1 is printed. To continue with the previous example of program return codes, use the following:

PROMPT_COMMAND='RET=$?; if [ $RET != 0 ] ; then echo "rc: $RET"; fi'
PS1='\$ ' 

This produces almost the same results as the previous example, except that executing programs with exit code 0 won't print an additional blank line now.

To reset PROMPT_COMMAND again, you can assign it an empty value:

PROMPT_COMMAND= 

Complete example

The following prompt is an example on how you could combine the various elements I mentioned above into a powerful and informative prompt. I've been using this prompt, or slight variations of it, for a few months now.

I'll start with PROMPT_COMMAND, as its output appears first:

PROMPT_COMMAND='RET=$?; echo; if [ $RET != 0 ] ; then echo "rc: $RET"; fi; if [ "$PWD" != "$HOME" ]; then if [ $( ls -A | wc -l ) -lt 20 ]; then ls -mAF; fi; else ls -mF; fi'
  • This reads the last command's return value, and if non-zero, prints it on a separate line.
  • If the current working directory is not the home directory, and has fewer than 20 files and folders in it, list them. If the current working directory is the home directory, only list non-hidden files and folders.

The prompt itself:

PS1='\[\033[0;32m\]\u\[\033[00m\] in \[\033[0;32m\]$( pwd ) ($( OUT=$( ls -A | wc -l ); echo $OUT ) entries, $(( $( ls -A | wc -l ) - $( ls | wc -l ) )) hidden)\n\[\033[1;32m\]\# \! \$\[\033[;m\] '
  • Print the user name,
  • the full current working directory (i.e. no ~ instead of /Users/danielbeck),
  • the numbers of both all and hidden files and folders in the current working directory,
  • and on the next line, print the command and history numbers, and the root privileges indicator $/#.

On my Mac, it looks like this:

Screenshot of prompt previously explained

Through color highlighting, the prompt is easily visible between program invocations with lots of output. It prints the previous command's return code unless it's 0, and lists all files in the current directory if the file count is reasonably low. I prefer this to functions that combine cd and ls, as I can immediately see the changes to the current directory after program execution.

Persisting changes

As I wrote at the beginning, your prompt is not kept across shell sessions when you simply set PS1 (orPS2) in your shell. To keep your changes across sessions and restarts of your machine, add the relevant assignments of PS1, PS2 and PROMPT_COMMAND -- one per line -- to your .bash_profileor .bashrc file in your home directory (more on these files). Save the file, open a new bash session, and enjoy your custom prompt!

7 Comments

Subscribe to comments with RSS.

  • $(pwd) does not update when you change directories.

  • Christian says:

    Yeah, those scripts are run exactly when the variable is assigned, whether that be in the interactive shell or the ~/.bashrc profile. Does anyone know what a standard install of Ubuntu uses for $PS1?

  • danielbeck says:

    Craig, Christian: Did you enclose the prompt assignment in single quotes as written in the article? If you did, the script snippets within shouldn’t be evaluated immediately.

  • Christian says:

    Ah! I was enclosing it in backticks (`). Enclosing the definition in single quotes (‘) produced expected behavior.

  • danielbeck says:

    Glad that worked. I don’t use back ticks for inline scripts anymore. Someone on SU taught be to use $( and ) instead — I find it more readable anyway, and it’s usually easier to type, especially if you consider different keyboard layouts.

  • danielbeck says:

    I just edited this post to properly display all command line examples and the escape sequences for color formatting. I apologize for the long time the wrong examples were up.

  • Leave a comment

    Log in
    with Stack Exchange
    or