I have been using the fish shell for the past six months and have been loving every minute of it. I wanted to tell you about it because it is vastly simpler to use than I ever felt both bash and zsh ever were.

Why do I use my shell?

I use my shell mostly for day to day programming work. Lots of boring directory changes, grepping output, piping commands together, etc. I also write a fair amount of python and need to switch between virtualenvs frequently.

What do I want?

Autocomplete should work excellently. The shell should feel quick and responsive. The default configuration language for my shell should be simple and obvious. Global variables should be used sparingly if at all. Reading code written for the shell should not make me want to cry.

Why do I like fish?

fish has some excellent defaults that feel natural from the start.

It has bar none the best tab/auto-completion I have ever used. The completions are actively filtered as you type. If the command has a description, then fish displays this as well. For example, typing brew c<TAB> produces the following


fish also uses a sane and simple configuration language in which paths can be set easily. For example, here is my fish.config file in which I’ve setup pyenv, homebrew and the default paths.

set default_path /usr/bin /usr/sbin /bin /sbin
set homebrew /usr/local/bin /usr/local/sbin
set brew_pyenv /usr/local/var/pyenv/shims
set -gx PATH $homebrew $brew_pyenv $default_path

With fish, the convention is only to save functions that will always be evaluated on shell startup in the main config.fish file. All functions are defined individually and saved inside of ~/.config/fish/functions. fish comes with an in-shell editor that makes it easy to both create and update functions. funced is the in-shell editor and I have found it to be indespenible. To edit any function attached to the fish shell, you just type

funced function_name

This will open up a simple editor allowing you to edit the function in place. New functions can be defined using funced as well. To save a function permanently; that is longer than the life of your current shell, funcsave needs to be used:

funcsave function_name

This updates the persisted copy of the function or creates a new function inside of ~/.config/fish/functions

Here is a picture of funced in action.


In addition to the wonderful handling of user defined functions, the prompts included with fish are excellent. By default, fish displays only the first letter of each parent directory’s name in relation to the present working directory, e.g.:


The prompt can be edited several ways. The easiest way to edit the prompt is to open up the web-based configuration utility. I’m normally not a fan of using web configurations, bit fish’s is simple and straigtforward. To access the configuration utility, type


This takes you to a page where the prompt can be chosen easily. I just use the classic + git option.

The other way is to edit the prompt directly. This can be done by editing the fish_prompt.fish file which is conveniently saved in ~/.config/fish/functions.

I love using fish because it doesn’t surpise me. It has excellent user documentation, makes itpleasent to define new functions, and is extremely easy to reconfigure. If you haven’t done so yet, I’d highly recommend giving fish a try; installation directions can be found on the fish homepage.