Customizing the Bash prompt: PS1, colors, Git branch and status
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:
| Escape | Shows |
|---|---|
\u | user name |
\h, \H | host name up to the first dot / full host name |
\w, \W | current directory with ~ for home / only its last part |
\$ | # for root, $ for everyone else |
\t, \A | time as HH:MM:SS (24-hour) / HH:MM |
\D{format} | time in any strftime format, e.g. \D{%a %H:%M} |
\j | number of background jobs |
\!, \# | history number / command number |
\n | newline (for a two-line prompt) |
\e | escape 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:
"${PROMPT_COMMAND[@]}"keeps anything your distribution already put there, such as the terminal's directory tracking.${__last_status:+…}prints the colored part only when the variable is not empty.GIT_PS1_SHOWDIRTYSTATEadds*for unstaged and+for staged changes.GIT_PS1_SHOWUNTRACKEDFILES=1adds%, andGIT_PS1_SHOWUPSTREAM=autoshows<,>or=against the upstream branch. Each check costs time in huge repositories.- The fallback
__git_ps1() { :; }keeps the prompt working on machines without the helper.
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
selectmenu 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
- Long commands overwrite the start of the line: an escape sequence is missing its
\[ … \]. - The prompt shows
\e[32mliterally: you are not running Bash (check withecho "$0"); other shells such as dash or zsh use different prompt syntax. - The directory does not update: you used double quotes, so
$PWDwas expanded once. Use\win single quotes. - Changes vanish in new terminals: the terminal starts a login shell that reads
~/.bash_profilebut not~/.bashrc. Make sure~/.bash_profilecontains[[ -r ~/.bashrc ]] && . ~/.bashrc. - No colors at all: check
echo "$TERM"andtput colors; aTERMofdumborvt100means no color.
Sources
- Controlling the Prompt, Bash Reference Manual (GNU Project)
- Bash Variables (PROMPT_COMMAND, PROMPT_DIRTRIM, PS0–PS4), Bash Reference Manual
- bash(1) manual page (GNU Bash 5.3), man7.org
- Bash NEWS file (PS0 in 4.4, PROMPT_COMMAND arrays in 5.1), Chet Ramey
- console_codes(4): SGR color codes and OSC title sequences, man7.org
- tput(1) manual page, man7.org
- terminfo(5) manual page, man7.org
- git-prompt.sh (__git_ps1), Git source repository
- git-core file list for Fedora 45 (git-prompt.sh path), Fedora mdapi
- git package file list, Arch Linux
- /etc/bashrc as shipped by Fedora's setup package
- Bash/Prompt customization, ArchWiki
- Starship and Starship configuration