Tired of endlessly tweaking your shell configuration? Fish offers a refreshingly opinionated and powerful experience right out of the box.

I love the Fish shell for several reasons, the first being the out-of-box experience is best in class. You don’t need to go crazy with plugins or hell even a status line tool like oh-my-zsh. You can just get to work. Fish comes with so many nice features that make moving around in the terminal so pleasant.

Another reason is configuration. Yes, Fish has its own scripting language which makes it not POSIX compliant. Most make this out to be a bigger deal than it actually is. People complain about it being a poor choice for scripting and issues with portability. The solution to both of these, is to use fish-lang for only its designed purpose.

System-level scripts should be written in sh/bash for portability. Your shell config is personal.

As for integrating with tools most tools out there have fish integrations, so using fish is almost never actually an issue. Tools like python virtual environments create activate scripts in fish if that’s the shell they detect you using. And if you’re using a tool written entirely in bash you have two options:

  • Find an alternative written for fish or in a real programming language and shell agnostic.
    • e.g. for node’s nvm the readme explains the 5 alternatives, but you could also just use volta (rustBTW).
  • Use bass for interoperability.

Additionally, if you don’t want a complex config, you can use the command fish_config which will give you a web-ui for customizing: themes, prompt, functions, and keybindings in a jiffy. This lets you get up and running with fish even faster.

Autocomplete

I just want to show a quick example of fish’s game changing auto-complete.

Say you were to try this command in git:

# First some setup
git checkout -b something-foo-bar-spike-feature-final;
git checkout main;
# Try this in bash, zsh, and fish
git checkout spike # press tab after spike

Only fish will give you an autocomplete result (out of the box). Because unlike bash and zsh, fish will fuzzy find and has built-in git integration.

General practices

All my fish configs are written in such a way that I make no assumptions about what is installed. This is both so that each machine can be using all or none of my preferred apps and I still get access to my fish config. Before I use any command/app/tool, I check that it exists.

if type -q some_command
    # Configure some_command
end

My configuration

I got tired of having to be careful not to commit things that were appended to my fish config file, so I decided I’d move it to the conf.d directory.

from: $HOME/.config/fish/config.fish
to:   $HOME/.config/fish/config.d/config.fish

Turns out conf.d is loaded before the main config file anyways, so this changes basically nothing.

├── conf.d/
   └── config.fish
└── config.fish # This file is gitignored

As a result now the default config file can remain as a local config file. Any tool that wants to append things can do so and I don’t have to care.

At any time I can look at my local config and move some of those into a conf.d file.

conf.d directory

This is a feature of Fish, so you don’t need to source this folder or anything. This directory contains specific tool/app configurations. This keeps my config file more specific to shell configurations.

Generally, if a tool appends a blob of code to my config file, that’s a good candidate for something to go into a separate conf.d file. So another way of viewing files besides config.fish, is that they’re all code-gen’d. Where as config.fish is code I’ve written.

functions

Another great feature of fish, each file named after a command is a sort of lazy-evaluated script. All files here are only loaded when the function is called. So e.g. if I never uses bass, it’ll never be evaluated.


Command line tools I use with Fish

More details https://github.com/metruzanca/dotfiles/tree/master/home/.config/fish

I use starship.rs as my status line. Starship has very similar opinions to fish. The out-of-box experience is superb and configuration is very simple.

I use zoxide as an improved cd which lets me just teleport to places I’ve previously been to.

I use lsd as a fancy ls replacement. And I hook into fish’s fish_prompt event to call ls after changing directories so I always get a list as I move (which eliminates most calls of lsd).

My prompt starts up with pfetch because its pretty.