A modern, ergonomic Unix shell configuration with Fish
Tags: fish, unix, linux, shell
Published: 2021-11-21 18:11:28

---
title: A modern, ergonomic Unix shell configuration with Fish
published: true
description: How to use a modern shell like Fish together with tools like Starship and Exa to bring a modern ergonomic experience to the Unix shell.
tags: fish, unix, linux, shell
cover_image: https://source.unsplash.com/rEM3cK8F1pk
---

Like many others, I spend enough time in the shell that even small improvements can yield huge productivity gains. This post is an attempt to catalog several of quality-of-life enhancements I've made over recent months to my shell setup.

## History

Like most folks I know, I began my command-line life with good 'ol Bash and actually still do most of my shell scripting in Bash. Even if I eventually rewrite the command in another language, many of my tools begin life as a Bash script.

At some point, perhaps ~10 years ago, I was introduced to ZSH with the venerable [Oh My ZSH](https://ohmyz.sh/) framework. For me, this was a huge awakening because it taught me about how much shell improvements can impact your life. I gradually evolved my ZSH setup to use package managers like [antigen](https://github.com/zsh-users/antigen) and began to invest more in creature comforts.

Since that time, while the productivity gains have compounded, friends recommended Fish to me, the Friendly Interactive SHell. For my workflows this was the major boost which catapulted my shell configuration from "good enough" to actually "working well".

## Why Fish?

The first thing I noticed was that the default configuration of Fish was equivalent to about 30-45 lines of ZSH config. So even starting fresh with Fish was already roughly on-par with my previous ZSH configuration. Moreover, I find that the modern Fish dialect, especially its error-case semantics, are vastly superior considering the frequency with which things go wrong. In this way, Fish gives me a much more solid foundation on which to build a proper environment for myself.

## Diving into my Fish config

The core of any Fish config is the `config.fish` file found at `~/.config/fish/config.fish` on most systems.

Mine looks something like this:

```
#!/usr/bin/env fish

# Configure my shell's prompt
starship init fish | source

# Make it easy to switch to commonly-used directories
zoxide init fish | source

# Set up my ruby environment with rbenv
status --is-interactive; and source (rbenv init -|psub)
set -gx PATH "$HOME/.rbenv/bin" $PATH

# Use VS Code as the default editor and block until the file is closed
set -x EDITOR 'code --wait'

# Make skim do the right thing by default
set -x SKIM_DEFAULT_COMMAND "fd --type f"

## Elixir and Erlang
# Elixir package timeout to prevent stalled downloads
set -x HEX_HTTP_TIMEOUT 59
# Elixir IEx history config
set -x ERL_AFLAGS '-kernel shell_history enabled'

## Rust
# Enable a shared Cargo target directory to cut down on disk usage
set -x CARGO_TARGET_DIR $HOME/.cargo/shared_target
# Ensure that Cargo binaries are in the PATH
set PATH $HOME/.cargo/bin/ $PATH

# Helpful expansions
abbr -a -g gp git push
abbr -a -g gc git commit -asm
abbr -a -g gst git status

# Helpful aliases
command -q exa; and alias la="exa -abghl --git --color=automatic"
command -q exa; and alias ll="exa -bghl --git --color=automatic"

# Fisher!
if not functions -q fisher
    set -q XDG_CONFIG_HOME; or set XDG_CONFIG_HOME ~/.config
    curl https://git.io/fisher --create-dirs -sLo $XDG_CONFIG_HOME/fish/functions/fisher.fish
    fish -c fisher
end

## Node
# Volta config
set -gx VOLTA_HOME "$HOME/.volta"
set -gx PATH "$VOLTA_HOME/bin" $PATH
```

### Analysis

In order of appearance, here are some of the tools which feature in the above Fish config:

* The [Starship](https://starship.rs/) prompt is a terrific alternative to slower prompts written in pure shell. It supports a whole variety of status features, is quite performant, and has easy-to-understand configuration options.
* I use [zoxide](https://github.com/ajeetdsouza/zoxide) to quickly switch to recently used directories, or to paths which I use particularly commonly. It can be hard to remember to use `z` instead of `cd` but once you get the hang of it you'll never go back.
* My setup relies heavily on [rbenv](https://github.com/rbenv/rbenv) since I use Ruby for automation all over the place.
* Tools like [skim](https://github.com/lotabout/skim) work great with others like [fd](https://github.com/sharkdp/fd) to provide additional functionality when it comes to locating files and filtering through search results. I also use skim's `sk` command pretty heavily in other scripts when interactivity is required.
* Even though `ls` is a good-old standby, I prefer the aliases for `ll` and `la` that rely on [exa](https://the.exa.website/). These aliases default to showing important information like git status and can even prevent me from needing to launch subsequent, exploratory commands.
* I really fancy the [Fisher](https://github.com/jorgebucaran/fisher) package manager for installing Fish libraries, tools, and commands!
* Since I'm a big fan of toolchains as code, I've fallen in love with [volta](https://volta.sh/) for managing my Node.js and Yarn versions. Don't forget commands like `volta pin` in Node.js projects to lock in versions to your `package.json` file!

## Conclusion

So I guess you could say that Fish really hooked me. Get it? Anyway, thanks for reading this post and feel free to reach out to me with your favorite shell tricks and quality-of-life improvements! I really love this stuff and would love to hear from you.