Customize shell prompt
Tags: linux, bash, tutorial
Published: 2019-05-24 17:37:15

Change the appearance of the terminal [shell prompt](https://en.wikibooks.org/wiki/Guide_to_Unix/Explanations/Shell_Prompt): customize colors, text formatting, and dynamically display other type of information (including git status).

We're going to use bash on Ubuntu but most concepts can also be applied in other unix-based systems (e.g. MacOS and Windows Subsystem for Linux).

## Prompts variables

Bash has four type of prompts controlled by those variables:

- `PS1` primary prompt.
- `PS2` displayed when you've to type more commands (multi-line commands).
- `PS3` displayed when the select command is waiting for input.
- `PS4` displayed when debugging Bash scripts.

If you `echo $PS1` you'll see a bunch of characters sequences:

```txt
\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$
```

When you enter an interactive shell session, the shell read `PS1` and output something like this:

```txt
username@hostname:directory$
```

The dollar `$` symbol at the end signifies that you're a normal user, for root user it's replaced with the hash `#` symbol.

If you `echo $PS2` it'll display only the greater-than sign `>` symbol.

You can see both `PS1` and `PS2` in the screenshot below:

![](https://user-images.githubusercontent.com/24630716/54070240-cff76700-425d-11e9-963c-6ad444de4146.png)

The `PS3` and `PS4` prompts are not very common. In this guide we'll focus on the primary prompt.

## Change the prompt

To control the output of your primary prompt, edit the `PS1` variable:

```sh
PS1='my_prompt: '
```

![](https://user-images.githubusercontent.com/24630716/54074429-bf151880-4292-11e9-97bf-8a4a2cf98308.png)

Because `PS1` was replaced directly in Bash, changes will disappear on shell `exit`. Later in this guide we'll learn how to make this changes permanent.

## Embedding commands

You can run commands inside `PS1` variable:

```sh
PS1='$(exit_status=$? ; if test $exit_status -ne 0 ; then echo "(err)" ; fi)my_prompt: '
```

If exit code is not equal to `0` it'll display `(err)`.

![](https://user-images.githubusercontent.com/24630716/54114732-555c5200-43eb-11e9-9e7b-f5cb70b0e96e.png)

## Backslash-escaped characters

There are some backslash-escaped special characters you can use to dynamically display useful information in the prompt.

For example:

```sh
PS1='\t \u \h \w \$ '
```

- `\t` display time.
- `\u` display user name.
- `\h` display hostname.
- `\w` display current working directory.
- `\$` display dollar *$* symbol if you're normal user; display hash *#* symbol if you're root user.

![](https://user-images.githubusercontent.com/24630716/54074447-dce27d80-4292-11e9-9792-800e95b857f6.png)

Here is a complete list of [backslash-escaped characters](https://ss64.com/bash/syntax-prompt.html).

## ANSI escape sequences

Bash allows the user to call a series of [ANSI escape sequences](https://en.wikipedia.org/wiki/ANSI_escape_code) to change colors, text formatting, cursor location and other options of the terminal window. Those sequences are a set of non-printable [control characters](https://en.wikipedia.org/wiki/ASCII#Control_characters) which the shell interprets as commands.

An ANSI escape sequence always start with an escape character and a left-bracket character, followed by one or more control characters:

```txt
ESC[COMMAND
```

- The escape character `ESC` can be written as `\033` or `\e` or `\x1b`.
- `COMMAND` is the control character.

See the [list of ANSI sequences](https://bluesock.org/~willkg/dev/ansi.html#sequences) for all available commands (some terminals may have partial support for ANSI sequences).

## Colors and text formatting

To colorize the output of your text terminal use the following ANSI sequence:

```txt
ESC[CODEm
```

Where `CODE` is a series of one or more semicolon-separated color codes.

For example:

```sh
echo -e "\033[44mHello World\033[0m"
```

- `-e` enable `echo` to parse escape sequences.
- `\033[` mark the start of an ANSI sequence.
- `44` is the code for background color blue.
- `m` mark the end of color codes.
- `0` removes all text attributes (formatting and colors). It's important to reset attributes, otherwise the styles will be applied to all texts after *Hello World* (including the prompt and the text we type).

![](https://user-images.githubusercontent.com/24630716/54041392-e7841080-41c7-11e9-8d1c-52ac7bc0e168.png)

You can also modify a color by setting an "attribute" before its base value, separated by a `;` semi-colon.

So if you want a green background (`46`) with underline text (`4`), the sequence is:

```sh
echo -e "\033[4;46mHello World\033[0m"
```

![](https://user-images.githubusercontent.com/24630716/54275170-4069f500-458a-11e9-81fc-562e18ebabef.png)

You can combine multiple sequences together:

```sh
echo -e "\033[41m\033[4m\033[1;33mHello World\033[0m"
```

![](https://user-images.githubusercontent.com/24630716/54275994-698b8500-458c-11e9-9177-f966510776b5.png)

Here you can find a list of all [color codes](https://stackoverflow.com/a/33206814/).

## Save prompt changes permanently

To make the changes permanent you can modify the default `PS1` variable or add a new one at the end of *~/.bashrc* file:

```sh
PS1='$(exit_status=$? ; if test $exit_status -ne 0 ; then echo -e "(\[\033[31m\]err\[\033[0m\])${debian_chroot:+($debian_chroot)}" ; else echo ${debian_chroot:+($debian_chroot)} ; fi)\[\033[1;33m\]\u@\h\[\033[0m\]:\[\033[1;36m\]\w\[\033[0m\]\$ '
```

Non-printing characters must be surrounded with escaped square brackets `\[` (start of non-printing characters) and `\]` (end of non-printing characters). For example `\[\033[1;33m\]`. Otherwise Bash think they're printing characters and use them to calculate its size (cause the text to wraps badly before it gets to the edge of the terminal).

This line `${debian_chroot:+($debian_chroot)}` display to your prompt an indication of which [chroot](https://unix.stackexchange.com/a/3174/) you're in.

You can use `source ~/.bashrc` to refresh the changes instead of exit and re-enter the shell.

![](https://user-images.githubusercontent.com/24630716/54270455-c2085580-457f-11e9-9e7e-16d9a5ad0b3e.png)

## Display git repository status

Git provide a script that allows to see repository status in your prompt.

Download [git-prompt.sh](https://github.com/git/git/blob/8976500cbbb13270398d3b3e07a17b8cc7bff43f/contrib/completion/git-prompt.sh):

```sh
curl -o ~/.git-prompt.sh https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh
```

The script comes with a function `__git_ps1` that can be used in two ways:

- You can call it inside `PS1` (this way you can't see colored hints).
- Or you can call it inside [`PROMPT_COMMAND`](https://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x264.html) (with this method you can enable colored hints).

By default `__git_ps1` will show only the branch you're in, you can also enable git status through a series of variables whose names start with `GIT_PS1_SHOW*`.

If you don't want colored hints you can simply use `$(__git_ps1 "(%s)")` inside `PS1` and sets `GIT_PS1_SHOW*` variables as you prefer.

To show colored hints you cannot edit `PS1` directly. Instead, you have to call `__git_ps1` function inside `PROMPT_COMMAND` variable. If `PROMPT_COMMAND` is set, the value is interpreted as a command to execute before the printing of primary prompt. In this mode you can request colored hints using `GIT_PS1_SHOWCOLORHINTS=true`.

Add the following code at the end of *~/.bashrc* file:

```sh
source ~/.git-prompt.sh

GIT_PS1_SHOWDIRTYSTATE="true"
GIT_PS1_SHOWSTASHSTATE="true"
GIT_PS1_SHOWUNTRACKEDFILES="true"
GIT_PS1_SHOWUPSTREAM="auto"
# Colored hints work only if __git_ps1 is called inside PROMPT_COMMAND
GIT_PS1_SHOWCOLORHINTS=true

PROMPT_COMMAND='__git_ps1 "$(exit_status=$? ; if test $exit_status -ne 0 ; then echo -e "(\[\033[31m\]err\[\033[0m\])${debian_chroot:+($debian_chroot)}" ; else echo ${debian_chroot:+($debian_chroot)} ; fi)\[\033[1;33m\]\u@\h\[\033[0m\]:\[\033[1;36m\]\w\[\033[0m\]" "\$ " "(%s)"'
```

- `source ~/.git-prompt.sh` will load *git-prompt.sh* script.
- `GIT_PS1_SHOW*` variables are used to add additional features.
- If `__git_ps1` is used inside `PROMPT_COMMAND` it must be called with at least two arguments, the first is prepended and the second appended to the state string when assigned to `PS1`. There is an optional argument used as [printf](https://ss64.com/bash/printf.html) format string to further customize the output (`%s` is the output of `__git_ps1` which in this case is wrapped in parenthesis).

![](https://user-images.githubusercontent.com/24630716/54270548-ec5a1300-457f-11e9-9dfb-dbe7e58bd569.png)

For more info read comments inside *~/.git-prompt.sh* file.

## External resources

- https://www.digitalocean.com/community/tutorials/how-to-customize-your-bash-prompt-on-a-linux-vps
- https://misc.flogisoft.com/bash/home
- https://wiki.archlinux.org/index.php/Bash/Prompt_customization
- https://medium.freecodecamp.org/how-you-can-style-your-terminal-like-medium-freecodecamp-or-any-way-you-want-f499234d48bc