Customizing the Bash prompt: PS1, colors, Git branch and status

Updated November 2026

The Bash prompt is the value of the PS1 variable. Set it in ~/.bashrc with a few backslash escapes for user, host and directory, wrap every color code in \[ … \], and use PROMPT_COMMAND for anything that needs to run before each prompt, such as showing the last exit code or the Git branch.

The examples work in Bash 5.1 and later (as of November 2026, Debian 13 and Ubuntu 24.04 ship 5.2 and Ubuntu 26.04 ships 5.3). The classic version of this HOWTO is kept by the LDP at tldp.org.

Where the prompt comes from

Interactive Bash shells read ~/.bashrc, and most distributions set a default PS1 there or in /etc/bash.bashrc (Debian, Ubuntu) or /etc/bashrc (Fedora, RHEL). Look at what you have now:

echo "$PS1"
declare -p PROMPT_COMMAND 2>/dev/null

Try a new prompt by assigning it in the running shell; nothing is permanent until you put the line at the end of ~/.bashrc. Reload with source ~/.bashrc or open a new terminal. If you break it badly, PS1='\$ ' gives you a plain prompt back.

Prompt escapes

Bash replaces these backslash sequences each time it prints the prompt:

Common PS1 escapes (full list in the Bash manual, "Controlling the Prompt")
EscapeShows
\uuser name
\h, \Hhost name up to the first dot / full host name
\w, \Wcurrent directory with ~ for home / only its last part
\$# for root, $ for everyone else
\t, \Atime as HH:MM:SS (24-hour) / HH:MM
\D{format}time in any strftime format, e.g. \D{%a %H:%M}
\jnumber of background jobs
\!, \#history number / command number
\nnewline (for a two-line prompt)
\eescape character, used to start color codes
\[ … \]marks characters that take no space on screen
PS1='\u@\h:\w\$ '          # ada@laptop:~/projects$
PS1='[\A] \W\$ '           # [14:05] projects$
PS1='\u@\h:\w\n\$ '        # directory on one line, command on the next
PROMPT_DIRTRIM=2           # \w shows .../doc/bash instead of a long path

Use single quotes when you assign PS1. With double quotes, Bash expands $variables and $(commands) once, at assignment time, and \$ turns into a plain $, so root would no longer see #.

Colors without broken line editing

Terminals change color when they receive an escape sequence: \e[, one or more numbers separated by ;, then m. 0 resets, 1 is bold, 30–37 set the foreground color (black, red, green, yellow/brown, blue, magenta, cyan, white), 40–47 the background, and 38;5;N picks color N from the 256-color palette.

PS1='\[\e[1;32m\]\u@\h\[\e[0m\]:\[\e[1;34m\]\w\[\e[0m\]\$ '

That gives a bold green user@host and a bold blue directory, the same style as Debian's and Ubuntu's default color prompt. The important part is \[ and \] around every escape sequence. They tell Bash those bytes take no space on screen. Without them Bash miscounts the prompt's width, and long commands wrap onto the same line, or the cursor jumps when you scroll through history.

If you prefer names to numbers, let tput look up the codes for your terminal in the terminfo database. Note the \\$: inside double quotes it becomes the \$ escape.

bold=$(tput bold) green=$(tput setaf 2) blue=$(tput setaf 4) reset=$(tput sgr0)
PS1="\[$bold$green\]\u@\h\[$reset\]:\[$bold$blue\]\w\[$reset\]\\$ "
unset bold green blue reset

tput colors tells you how many colors the terminal says it supports: 256 for xterm-256color, 8 on the Linux text console. See the terminal HOWTO for how TERM and terminfo work.

Exit code and Git branch

PROMPT_COMMAND runs just before each prompt is printed. Since Bash 5.1 it can be an array of commands. The function below must run first, because $? still holds the status of your last command only at that moment. Git ships a helper, __git_ps1, that prints the current branch; on Debian and Ubuntu it is installed with Git as /usr/lib/git-core/git-sh-prompt, on Fedora as /usr/share/git-core/contrib/completion/git-prompt.sh (in the git-core package) and on Arch as /usr/share/git/completion/git-prompt.sh. Elsewhere, download contrib/completion/git-prompt.sh from the Git source repository to ~/.git-prompt.sh.

# ~/.bashrc additions: status, user@host, directory, Git branch
if [[ -r /usr/lib/git-core/git-sh-prompt ]]; then
    . /usr/lib/git-core/git-sh-prompt          # Debian and Ubuntu
elif [[ -r /usr/share/git-core/contrib/completion/git-prompt.sh ]]; then
    . /usr/share/git-core/contrib/completion/git-prompt.sh    # Fedora
elif [[ -r /usr/share/git/completion/git-prompt.sh ]]; then
    . /usr/share/git/completion/git-prompt.sh  # Arch
elif [[ -r ~/.git-prompt.sh ]]; then
    . ~/.git-prompt.sh                         # downloaded copy
fi
declare -F __git_ps1 > /dev/null || __git_ps1() { :; }
GIT_PS1_SHOWDIRTYSTATE=1

__prompt_status() {
    local status=$?
    __last_status=
    (( status == 0 )) || __last_status=$status
}
PROMPT_COMMAND=(__prompt_status "${PROMPT_COMMAND[@]}")

PS1='${__last_status:+\[\e[31m\][$__last_status]\[\e[0m\] }'
PS1+='\[\e[32m\]\u@\h\[\e[0m\]:\[\e[34m\]\w\[\e[0m\]'
PS1+='$(__git_ps1 " (%s)")\$ '

After a failing command the prompt starts with a red [1] (or whatever the status was); inside a Git work tree with uncommitted changes it ends with (main *). Notes:

Keep the prompt fast. Everything in PS1 and PROMPT_COMMAND runs before every prompt. A command that touches the network or scans a big directory makes the shell feel slow. Measure with time __git_ps1 inside a repository.

Window title, PS0 and the other prompts

Most terminal emulators set their window or tab title from the sequence \e]0;text\a. Debian's default ~/.bashrc already does this for xterm-like terminals. To add it yourself, put it inside \[ … \] because it prints nothing visible:

PS1='\[\e]0;\u@\h: \w\a\]'"$PS1"

Bash has four more prompt variables:

PS0 (Bash 4.4 and later)
Printed after you press Enter and before the command runs. PS0='started \t\n' stamps the start time of every command.
PS2
The continuation prompt shown when a command spans lines; the default is > .
PS3
The prompt of the select menu command.
PS4
The prefix of trace lines from set -x; the advanced scripting HOWTO shows a useful debugging value.

A different prompt for root

\$ already shows # for root. To make root shells impossible to miss, give root a red prompt in /root/.bashrc:

PS1='\[\e[1;31m\]\u@\h\[\e[0m\]:\w\$ '

sudo -i starts a root login shell, which reads root's startup files and so picks up this prompt.

Starship and other prompt programs

If you want a rich prompt (language versions, cloud contexts, command duration) without writing it yourself, Starship is a widely used prompt program that works in Bash, Zsh, fish and other shells. Install it from your distribution (Debian 13 has a starship package in main) or from its website, then add this as the last line of ~/.bashrc:

eval "$(starship init bash)"

It is configured in ~/.config/starship.toml. Starship's documentation lists a Nerd Font as a prerequisite; without one, some symbols may not display. Starship sets the prompt itself, so remove or comment out your own PS1 and PROMPT_COMMAND lines first.

Troubleshooting

Sources