My ultimate shell setup with Fish shell and Tmux (Part 1)
I’ve recently been asked to share my shell setup, which if you ask me, that’s one of the highest levels of praise you can achieve as a software developer 🤣 In this article, I’ll describe the setup I’ve been using for the past few years, and how to achieve similar results yourself.
🖼️ The result
First, let’s look at what we’ll try to achieve by looking at a screenshot I took last year when showing off to a senior co-worker of mine. Visually speaking, almost nothing changed since then, so let me just be lazy for a while and re-use this overly annotated screenshot.
I think some people go for very minimalist shell setups, and I suppose you could say that I prefer the exact opposite of minimalist in this regard 😅 The screenshot above is missing two significant elements, which would be the exit code of previously executed command and presence of background processes - not to worry, these elements are not omitted, just hidden in case no interesting information is available to show (last process terminated successfully/no background processes are running respectively).
👆 Prerequisites
To achieve similar results, I believe you’ll need two pieces of puzzle that I’ll not cover in detail in this article:
- Capable terminal emulator (macOS’
Terminal.app
doesn’t qualify) – I’ve been living in the iTerm2 land since I migrated to macOS, always configured the superior Solarized Dark color theme; - Nerd-patched font for all the special glyphs that our shell will show us – either patch a font of your liking yourself or go with one of the pre-patched fonts; popular choices include FiraCode, Hack, Inconsolata, Iosevka and others.
🐟🐚 It all starts in the shell…
After using bash
for many years and zsh
for many more years, I switched to fish
shell a few years ago. While zsh
is a fine shell, I felt that fish
can be more user-friendly and easier to customize – but I recognize that this doesn’t come without drawbacks, namely that fish
shell doesn’t respect the traditional POSIX syntax so you’ll have to definitely alter some of your habits. Let’s first install fish
shell and then I’ll share a few tricks to get more familiar with fish
shell.
brew install fish
echo /usr/local/bin/fish | sudo tee -a /etc/shells
chsh -s /usr/local/bin/fish $(whoami)
Say “bye-bye” to bash
/zsh
because the next time you open a terminal, you should be welcomed by a friendly interactive shell, like so:
Next, install Oh My Fish, which is essentially a package manager for Fish shell, along with some essential plugins and themes:
curl -L https://get.oh-my.fish | fish
omf install bobthefish # Theme
omf install aws # AWS integration & command completion
omf install bass # Allows running heavily Bash-dependant utilities, like nvm
omf install brew # Integrate Homebrew paths
omf install colored-man-pages
omf install export # Bring back Bash-like export command
omf install https://github.com/jhillyerd/plugin-git
omf install osx # Finder/macOS integration and utilities
omf install wifi-password
omf install z # Autojump implementation
For more information about what these do, check the package list – for example, wifi-password
shows you the current WiFi password – not something you need every day, but when you need it, it’s very nice to have; z
allows you to quickly jump across often visited directories just by typing part of their names; and the git
plugin (documentation available here) gives you more git
aliases and abbreviations than you could ever remember.
You’ll notice that even with the bobthefish
theme installed and automatically activated, your shell prompt still looks way different from the first screenshot I presented – so let’s configure the shell and prompt. Listed below is the content of the ~/.config/fish/config.fish
file I currently use, but feel free to adjust it to your heart’s content:
set -gx COLORTERM truecolor
set -gx EDITOR nvim
set -gx LANG cs_CZ.UTF-8 # Adjust this to your language!
set -gx LC_ALL cs_CZ.UTF-8 # Adjust this to your locale!
set -gx VIRTUAL_ENV_DISABLE_PROMPT true
set -gx GOPATH $HOME/go
set -x PATH $GOPATH/bin $HOME/.composer/vendor/bin $HOME/Library/Python/3.7/bin $PATH
set -gx HOMEBREW_AUTO_UPDATE_SECS 86400
set -gx DOCKER_BUILDKIT 1
set -gx COMPOSE_DOCKER_CLI_BUILD 1
set -g fish_key_bindings fish_vi_key_bindings
set -g fish_bind_mode insert
# Title options
set -g theme_title_display_process yes
set -g theme_title_display_path yes
set -g theme_title_display_user yes
set -g theme_title_use_abbreviated_path yes
# Prompt options
set -g theme_display_ruby yes
set -g theme_display_virtualenv yes
set -g theme_display_vagrant no
set -g theme_display_vi yes
set -g theme_display_k8s_context no # yes
set -g theme_display_user yes
set -g theme_display_hostname yes
set -g theme_show_exit_status yes
set -g theme_git_worktree_support no
set -g theme_display_git yes
set -g theme_display_git_dirty yes
set -g theme_display_git_untracked yes
set -g theme_display_git_ahead_verbose yes
set -g theme_display_git_dirty_verbose yes
set -g theme_display_git_master_branch yes
set -g theme_display_date yes
set -g theme_display_cmd_duration yes
set -g theme_powerline_fonts yes
set -g theme_nerd_fonts yes
set -g theme_color_scheme solarized-dark
bind -M insert \cg forget
if which asdf > /dev/null; status --is-interactive; and source (brew --prefix asdf)/asdf.fish; end
if which direnv > /dev/null; direnv hook fish | source; end
if which goenv > /dev/null; status --is-interactive; and source (goenv init -|psub); end
if which rbenv > /dev/null; status --is-interactive; and source (rbenv init -|psub); end
if which swiftenv > /dev/null; status --is-interactive; and source (swiftenv init -|psub); end
Line 42 binds a forget
function to the key combination of Ctrl-G
, so let’s create the forget
function right now by pasting the following into ~/.config/fish/functions/forget.fish
:
function forget
set -l cmd (commandline | string collect)
printf "\nDo you want to forget '%s'? [Y/n]\n" $cmd
switch (read | tr A-Z a-z)
case n no
commandline -f repaint
return
case y yes ''
history delete --exact --case-sensitive -- $cmd
commandline ""
commandline -f repaint
end
end
What this does is allows you to easily remove command from shell’s history, for example if you typed sensitive information or made a typo in a command that you don’t want to get reminded of in the future. Activate it by going up the history with the up arrow key ⬆️⌨️ until you hit the command you wish to forget, then activate the function by pressing Ctrl-G
. Speaking of fish
functions, here’s one more you might find useful – get a random Git-related tip as spoken with a random pony anytime you open a new shell! First, let’s install the dependencies:
brew install ponysay
npm install -g git-tip
Then put the following text into ~/.config/fish/functions/fish_greeting.fish
:
function fish_greeting
git-tip | ponysay
end
If you followed the guide properly (and if I haven’t missed anything), your terminal should now look fairly similar to mine!
🦺 Fish survival guide
As I mentioned at the beginning of the article, Fish is often different. I hope that this little table will help you overcome the most problems you could face:
bash command |
fish equivalent |
Notes |
---|---|---|
true && true |
true; and true |
|
true || false |
true; or false |
|
echo "Date: $(date)" |
echo "Date: "(date) |
|
export A=b |
set -gx A b |
You can keep using export with the OMF plugin we installed |
echo "A=${A}" |
echo "A="$A |
|
for i in $(seq 10); echo $i; done |
for i in (seq 10); echo $i; end |
|
Ctrl-R for history search |
Type search term, then press ⬆️ key | Or omit search term for linear history browsing |
For more in-depth survival guide, be sure to check the official documentation.
And since this article is getting quite long, let’s save the Tmux part for next time!