A better shell with oh-my-zsh
Tags: productivity, zsh, powerlevel10k, shell
Published: 2020-10-31 18:35:16

# A better shell with oh-my-zsh

## :question: Abstract

Not So Basic: Devenv is a series of tutorials where we aim to have a nice, productive and portable environment that take the best of both worlds between Linux and windows.  

In this second tutorial "A better shell with oh-my-zsh" we do the groundwork using:
 - Zsh: a Linux shell with extended capabilities (command-line completion, command history, theming and much more...).
 - Oh My Zsh: a framework for managing Zsh configurations and theming.
 - Powerlevel10k: A quick, flexible and powerful theme for Zsh.

## :memo: Overview

To complete this tutorial, you need to:

- Install [Zsh and Oh My Zsh](#zsh-and-oh-my-zsh)
- Install and configure [Powerlevel10k](#powerlevel10k)

## Zsh and Oh My Zsh

### Zsh

First you need to **run the following commands** to install zsh:

```
sudo apt update
sudo apt install zsh
```
### Oh My Zsh

Now you need to install [Oh My Zsh](https://ohmyz.sh).

**Run the following command** to install Oh My Zsh:

```
sh -c "$(curl -fsSLk https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended
```

### Change the default shell

The default shell is the shell used when you start your terminal, to use zsh with the configuration we just set up, run:

```
chsh -s $(which zsh)
```

Now run `exit`, it will close your current terminal, then open it again, you should be using zsh with your current theme.

### A look on the default configuration.

You can find the default configuration file is in your home folder: `~/.zshrc`

If you open it, you can see that most of it is commented.

A short version can be summarized as:

```bash=
# Path to your oh-my-zsh installation.
export ZSH="/home/USERNAME/.oh-my-zsh"

# Set name of the theme
ZSH_THEME="robbyrussel"

# Plugins
plugins=(git)

#import parent settings
source $ZSH/oh-my-zsh.sh
```

Themes can add new features and nice display improvements.

## Powerlevel10k

### Install

[Powerlevel10k](https://github.com/romkatv/powerlevel10k) is a full-featured theme for Zsh.

![It's over 9000! Dbz meme](https://i.pinimg.com/originals/c6/5d/42/c65d423e1bfdaa32304dfd4a1ff09800.gif)

This theme offers the possibility to customize the style of your shell using many elements based on a font that includes icons.

[Nerd fonts](https://github.com/ryanoasis/nerd-fonts) contains all the icons you need to have a good experience with the tool. You can check the features offered on the [official nerd fonts website](https://www.nerdfonts.com/), select the font you prefer from the [download page](https://www.nerdfonts.com/font-downloads), or use the [cheat sheet](https://www.nerdfonts.com/cheat-sheet) to search for an icon.

We will use [FiraCode Nerd Font Mono](https://github.com/ryanoasis/nerd-fonts/tree/master/patched-fonts/FiraCode) in this tutorial because it is a monospaced programming font including [typographic ligatures](https://www.hanselman.com/blog/monospaced-programming-fonts-with-ligatures) and well rendered by most of the terminals.

#### Install the recommended font on Windows

**Download** [FiraCode Nerd Font](https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/FiraCode.zip), then follow the [manual font installation](https://github.com/romkatv/powerlevel10k#manual-font-installation) instructions for your terminal. 

#### Install the recommended font on Linux

Create a `fonts` directory in `~/.local/share`
```bash
mkdir -p ~/.local/share/fonts
```

Then you can **download** `FiraCode Nerd Font`:

```bash=
wget https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/FiraCode/Bold/complete/Fira%20Code%20Bold%20Nerd%20Font%20Complete%20Mono.ttf -P ~/.local/share/fonts
wget https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/FiraCode/Light/complete/Fira%20Code%20Light%20Nerd%20Font%20Complete%20Mono.ttf -P ~/.local/share/fonts
wget https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/FiraCode/Medium/complete/Fira%20Code%20Medium%20Nerd%20Font%20Complete%20Mono.ttf -P ~/.local/share/fonts
wget https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/FiraCode/Regular/complete/Fira%20Code%20Regular%20Nerd%20Font%20Complete%20Mono.ttf -P ~/.local/share/fonts
wget https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/FiraCode/Retina/complete/Fira%20Code%20Retina%20Nerd%20Font%20Complete%20Mono.ttf -P ~/.local/share/fonts
wget https://github.com/ryanoasis/nerd-fonts/raw/master/patched-fonts/FiraCode/SemiBold/complete/Fira%20Code%20SemiBold%20Nerd%20Font%20Complete%20Mono.ttf -P ~/.local/share/fonts
```

Finally, you can follow the [manual font installation](https://github.com/romkatv/powerlevel10k#manual-font-installation) instructions for your terminal. 

#### Install powerlevel10k as a Oh-my-zsh theme

Verify that git is present on your distribution:
```bash
git --version
```

If git is not installed on your distribution, [follow these instructions to install it](https://git-scm.com/downloads), then **run**:

```bash
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}/themes/powerlevel10k
```

Set `ZSH_THEME="powerlevel10k/powerlevel10k"` in ~/.zshrc

```bash
sed -i 's/ZSH_THEME="[^"]*/ZSH_THEME="powerlevel10k\/powerlevel10k/g' .zshrc
```

### Configure

#### Using the configuration wizard

When you `exit` and go back to your shell, you will be prompted by Powerlevel10k configuration wizard, **follows the instructions to set up the configuration of your choice**.

If you want to start again the configuration wizard, run:
```bash
p10k configure
```

#### Manual advanced configuration

The configuration wizard is a nice starting point to quickly get a good-looking shell, but there is much more parameters you can fine-tune.

Here is an example of the result you can get at the end by following the [Customization examples](#customization-examples), which are taken from [our p10k configuration file](https://gist.github.com/marc-gavanier/2245a93c66b17564be55a5afe2705f8e):

![Custom shell](https://gitlab.com/codingones/articles/-/raw/master/not-so-basic/a-better-shell/shell-preview.png "Custom shell")

We will be glad to see the result of your own configuration, feel free to post a screenshot in the comments section!

You can use `vim` to explore and edit p10k configuration file, run the following command to install vim:

```bash
sudo apt install vim
```

Here are some basic vim commands:
- `i` to enter insert mode
- `escape` to exit insert mode
- `/` to enter a search term
- `enter` to perform search
- `n` to find the next occurrence or `N` to find the previous occurrence
- `escape` to exit insert mode, then `:wq` and `enter` to save the file and quit vim
- `escape` to exit insert mode, then `:q!` and `enter` to quit vim without saving
- Read [Getting started with Vim](https://opensource.com/article/19/3/getting-started-vim) to learn the basics of this powerful cli file editor 

Open p10k configuration file with vim:

```bash
vim ~/.p10k.zsh
```

#### Customization examples

##### Switch a prompt segment from a side to another

For this example we will add the context `user@hostname` on the left prompt side and the vcs (git) segment to the right side.

###### Left prompt segments

- Add `context`, not active by default on a local session, but we will change that later
- Remove `vcs` to display it with right prompt segments
```bash=
typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
...
context                 # user@hostname
...
)
```
###### Right prompt segments

- Remove `context` since it is now displayed with left prompt segments
- Add `vcs` 
- Add contextual information about tools. Only displayed when installed and referenced by content of the current directory: [Node.js](https://nodejs.org/), [dotnet](https://dotnet.microsoft.com/), [PHP](https://www.php.net/), [Laravel](https://laravel.com/), [package](https://www.npmjs.com/)
- Add information about hardware on the next line 
```bash=
typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
# =========================[ Line #1 ]=========================
vcs                     # git status
...
node_version            # node.js version
dotnet_version          # .NET version (https://dotnet.microsoft.com)
...
php_version             # php version (https://www.php.net/)
laravel_version         # laravel php framework version (https://laravel.com/)
...
package                 # name@version from package.json (https://docs.npmjs.com/files/package.json)
...
# =========================[ Line #2 ]=========================
...
load                    # CPU load
ram                     # free RAM
disk_usage              # disk usage
swap                    # used swap
battery                 # internal battery
wifi                    # wifi speed
...
)
```

##### Change common colors

If your terminal support hexColors, you can override with the format `var='hexCode'`  
In this example we: 
- Update some prompt segment background color
- Update the color of prompt char displayed on the left following the previous command execution result
- Update current directory colors
- Update exit code of the last command
- Update direnv status
```bash=
...
typeset -g POWERLEVEL9K_BACKGROUND='#232323'
...
typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND='#7ca661'
...
typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS,VIOWR}_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_DIR_FOREGROUND='#92b7d4'
...
typeset -g POWERLEVEL9K_DIR_SHORTENED_FOREGROUND='#44637a'
...
typeset -g POWERLEVEL9K_DIR_ANCHOR_FOREGROUND='#6fa0c6'
...
typeset -g POWERLEVEL9K_VCS_VISUAL_IDENTIFIER_COLOR='#6fa0c6'
...
typeset -g POWERLEVEL9K_VCS_LOADING_VISUAL_IDENTIFIER_COLOR='#6fa0c6'
...
typeset -g POWERLEVEL9K_STATUS_OK_FOREGROUND='#7ca661'
...
typeset -g POWERLEVEL9K_STATUS_OK_PIPE_FOREGROUND='#7ca661'
...
typeset -g POWERLEVEL9K_STATUS_ERROR_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_STATUS_ERROR_SIGNAL_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_STATUS_ERROR_PIPE_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_DIRENV_FOREGROUND='#6fa0c6'
...
```

##### Define VCS icons

You can override icons with the format `var='charCode'`
We create our own icons variables for the stashed/conflicted/staged/unstaged states.
In this example we: 
- Update untracked icon to replace default `?` with a [ghost from available font icons](https://fontawesome.com/icons/ghost?style=solid)
- Add a stash icon to replace default `*` with an [archive from available font icons](https://fontawesome.com/icons/archive?style=solid)
- Add a conflicted icon to replace default `~` with a [skull from available font icons](https://fontawesome.com/icons/skull?style=solid)
- Add a staged icon to replace default `+` with a [filled star from available font icons](https://fontawesome.com/icons/star?style=solid)
- Add an unstaged icon to replace default `!` with an [empty star from available font icons](https://fontawesome.com/icons/star?style=regular)
```bash=
...
# Change the value of this parameter to show a different untracked file icon.
typeset -g POWERLEVEL9K_VCS_UNTRACKED_ICON='\uf79f '

# Change the value of this parameter to show a different stashed file icon.
typeset -g POWERLEVEL9K_VCS_STASHES_ICON='\uf187 '

# Change the value of this parameter to show a different conflicted file icon.
typeset -g POWERLEVEL9K_VCS_CONFLICTED_ICON='\ufb8a '

# Change the value of this parameter to show a different staged file icon.
typeset -g POWERLEVEL9K_VCS_STAGED_ICON='\uf005 '

# Change the value of this parameter to show a different unstaged file icon.
typeset -g POWERLEVEL9K_VCS_UNSTAGED_ICON='\uf006 '
...
```

##### Change VCS colors and icons
We will edit the my_git_formatter() original function

- Update git status color
```bash=
...
function my_git_formatter() {
...
    if (( $1 )); then
      ...
      local      clean='%F{#6fa0c6\}'  # blue foreground
      local   modified='%F{#e8c273\}'  # yellow foreground
      local  untracked='%F{#e1874d\}'  # orange foreground
      local conflicted='%F{#d55555\}'  # red foreground
...
}
```

- Update git status constants to use the previously defined icons variables (later in function)
```bash=
...
function my_git_formatter() {
...
    (( VCS_STATUS_STASHES        )) && res+=" ${clean}${(g::)POWERLEVEL9K_VCS_STATUS_STASHES_ICON}${VCS_STATUS_STASHES}"
...
    (( VCS_STATUS_NUM_CONFLICTED )) && res+=" ${conflicted}${(g::)POWERLEVEL9K_VCS_CONFLICTED_ICON}${VCS_STATUS_NUM_CONFLICTED}"
...
    (( VCS_STATUS_NUM_STAGED     )) && res+=" ${modified}${(g::)POWERLEVEL9K_VCS_STAGED_ICON}${VCS_STATUS_NUM_STAGED}"
...
    (( VCS_STATUS_NUM_UNSTAGED   )) && res+=" ${modified}${(g::)POWERLEVEL9K_VCS_UNSTAGED_ICON}${VCS_STATUS_NUM_UNSTAGED}"
...
}
```

##### Package version format
- Update current node package version format `package-name:package-version`

```bash=
...
typeset -g POWERLEVEL9K_PACKAGE_CONTENT_EXPANSION='${P9K_PACKAGE_NAME//\%/%%}:${P9K_PACKAGE_VERSION//\%/%%}'
...
```

##### Hardware colors and configuration 

- Update disk usage colors
- Update disk usage levels thresholds
- Update disk usage display on warning
- Update free ram color
- Update used swap color
- Update CPU load colors
- Update battery colors
- Update wifi colors
- Update time colors
```bash=
...
typeset -g POWERLEVEL9K_DISK_USAGE_NORMAL_FOREGROUND='#97b882'
typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_FOREGROUND='#e1874d'
typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_DISK_USAGE_WARNING_LEVEL=80
typeset -g POWERLEVEL9K_DISK_USAGE_CRITICAL_LEVEL=90
...
typeset -g POWERLEVEL9K_DISK_USAGE_ONLY_WARNING=true
...
typeset -g POWERLEVEL9K_RAM_FOREGROUND='#af7ca4'
...
typeset -g POWERLEVEL9K_SWAP_FOREGROUND='#e1874d'
...
typeset -g POWERLEVEL9K_LOAD_NORMAL_FOREGROUND='#9090c0'
...
typeset -g POWERLEVEL9K_LOAD_WARNING_FOREGROUND='#e1874d'
...
typeset -g POWERLEVEL9K_LOAD_CRITICAL_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_BATTERY_LOW_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_BATTERY_{CHARGING,CHARGED}_FOREGROUND='#7ca661'
...
typeset -g POWERLEVEL9K_BATTERY_DISCONNECTED_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_WIFI_FOREGROUND='#9fbfb4'
...
typeset -g POWERLEVEL9K_TIME_FOREGROUND='#76a394'
...
```

##### Context colors

- Update context colors
- Update context display to be always shown by commenting the relevant line
```bash=
...
typeset -g POWERLEVEL9K_CONTEXT_ROOT_FOREGROUND='#db6e51'
...
typeset -g POWERLEVEL9K_CONTEXT_{REMOTE,REMOTE_SUDO}_FOREGROUND='#d55555'
...
typeset -g POWERLEVEL9K_CONTEXT_FOREGROUND='#e8c273'
...
# typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_{CONTENT,VISUAL_IDENTIFIER}_EXPANSION=
...
```

##### Development tools colors

- Update tools colors [Node.js](https://nodejs.org/), [nvm](https://github.com/nvm-sh/nvm), [package](https://www.npmjs.com/), [Rust](https://www.rust-lang.org/), [dotnet](https://dotnet.microsoft.com/), [Java](https://www.java.com), [Scala](https://scala-lang.org/), [PHP](https://www.php.net/), [Laravel](https://laravel.com/), [PostgreSQL](https://www.postgresql.org/)
```bash=
...
typeset -g POWERLEVEL9K_ASDF_NODEJS_FOREGROUND='#97b882'
...
typeset -g POWERLEVEL9K_ASDF_RUST_FOREGROUND='#e4917a'
...
typeset -g POWERLEVEL9K_ASDF_DOTNET_CORE_FOREGROUND='#b0b0d2'
...
typeset -g POWERLEVEL9K_ASDF_JAVA_FOREGROUND='#e8a478'
...
typeset -g POWERLEVEL9K_ASDF_POSTGRES_FOREGROUND='#c29cba'
...
typeset -g POWERLEVEL9K_ASDF_PHP_FOREGROUND='#92b7d4'
...
typeset -g POWERLEVEL9K_NODENV_FOREGROUND='#7ca661'
...
typeset -g POWERLEVEL9K_NVM_FOREGROUND='#7ca661'
...
typeset -g POWERLEVEL9K_NODEENV_FOREGROUND='#7ca661'
...
typeset -g POWERLEVEL9K_NODE_VERSION_FOREGROUND='#7ca661'
...
typeset -g POWERLEVEL9K_DOTNET_VERSION_FOREGROUND='#9090c0'
...
typeset -g POWERLEVEL9K_PHP_VERSION_FOREGROUND='#af7ca4'
...
typeset -g POWERLEVEL9K_LARAVEL_VERSION_FOREGROUND='#db6e51'
...
typeset -g POWERLEVEL9K_JAVA_VERSION_FOREGROUND='#e1874d'
...
typeset -g POWERLEVEL9K_PACKAGE_FOREGROUND='#97b882'
...
typeset -g POWERLEVEL9K_JENV_FOREGROUND='#e1874d'
...
typeset -g POWERLEVEL9K_PHPENV_FOREGROUND='#af7ca4'
...
typeset -g POWERLEVEL9K_SCALAENV_FOREGROUND='#e8a478'
```

##### Change cloud tools colors

- Update [Terraform](https://www.terraform.io/), [Kubernetes](https://kubernetes.io/), [aws](https://aws.amazon.com/) and [Azure](https://azure.microsoft.com) colors
```bash=
...
typeset -g POWERLEVEL9K_TERRAFORM_OTHER_FOREGROUND='#727299'
...
typeset -g POWERLEVEL9K_KUBECONTEXT_DEFAULT_FOREGROUND='#5a82a1'
...
typeset -g POWERLEVEL9K_AWS_DEFAULT_FOREGROUND='#ba7040'
...
typeset -g POWERLEVEL9K_AWS_EB_ENV_FOREGROUND='#ba7040'
...
typeset -g POWERLEVEL9K_AZURE_FOREGROUND='#92b7d4'
```
## Next article

In the next article [Improve your experience using git cli](https://dev.to/codingones/improve-your-experience-using-git-cli-4hih) we will focus on customizing git.

## Contributors

[Romain Cambonie](https://www.linkedin.com/in/romain-cambonie/)
[Marc Gavanier](https://www.linkedin.com/in/marc-gavanier/)

## References used

- [Oh My ZSH!](https://ohmyz.sh/)
- [Nerd Fonts](https://www.nerdfonts.com/)
- [Fira Mono](https://fonts.google.com/specimen/Fira+Mono)
- [Powerlevel10k](https://github.com/romkatv/powerlevel10k)
- [Font Awesome icons](https://fontawesome.com/)
- [.p10k.zsh configuration example](https://gist.github.com/marc-gavanier/2245a93c66b17564be55a5afe2705f8e)
- [Monospaced Programming Fonts with Ligatures](https://www.hanselman.com/blog/monospaced-programming-fonts-with-ligatures)