Menu

Hello Fish Shell

Three years ago I was becoming dissatisfied with my shell, and cast around for
alternatives.

Why? What caused the dissatisfaction? I was using Bash, and had been for
years. But, over time, I got tired of looking stuff up all the time. It seemed
like no matter how long I used Bash, I was never going to master it. Not
because I didn’t use it enough, but because it was full of arcana. Full of
concepts and ideas that were glued together without any coherence. An
encyclopedia of disparate facts to memorize.

Don’t get me wrong, Bash is a great shell. I still use it on my servers. It is a
successor to the Bourne Shell, and must maintain backwards compatibility with
it. That’s not a design flaw in Bash, but one of the keys to its success. But,
it comes with trade-offs. These days I’m willing to make different trade-offs.

Why Fish?

The shell language has a small syntax. There aren’t a lot of glyphs and
tokens to memorize. If you know any Ruby or Python, you’ll feel at home.

The features of the shell fit together well. Fish shell’s author spent some
time up front to think about the design of the shell and how its features would
fit together.

Features like syntax highlighting, tab-completion, auto-suggestions,
and auto-loading help my productivity in the shell.

Fish has sensible defaults. This results in a smaller configuration file for
me. I always like having less code to maintain.

An Example - The Prompt

As an example of the difference between Bash and Fish, let me show you how to
set the prompt in both shells. In these examples we’re going to set our prompt
to look like this:

username@hostname $

In Bash, you set the environment variable, PS1, to a string which you want to
be your prompt. Inside the string, you can put special characters which will
expand into a variable value like hostname.

PS1="\u@\h \$ "

In the above sample, the special characters, \u, expands to your user name,
and \h expands to your hostname. You have to escape the $ character,
because it has a special meaning. It is used to expand variables like, $HOME.

After explanation this seems pretty trivial, and it is. However, you can only use those special characters like \u when setting your prompt. This is an example of cognitive load in Bash. To set the prompt you need to learn a mini-language. Let’s compare to how it’s done in Fish.

To set the prompt in the Fish shell, you declare a function named,
fish_prompt. The output of this function should be a string, and that string
will be used as your prompt.

We’re using the printf command which takes a format string, and subsequent
arguments which fill in those placeholders. The argument (whoami) runs an
external command and returns the result. This functionality is equivalent to
backticks or the $(whoami) notation in Bash.

I prefer the way Fish sets the prompt. I find it easier to understand, and
easier to maintain over time. I don’t have to learn any extra concepts or
languages. It’s functions and subcommands all the way down. These two features
are composable and reusable.

What’s even nicer, is Fish can autoload functions. If you put this function in
the correct directory, Fish will find it, and load it for you. It’ll even
reload it for you when you change it! To set the prompt, I’d put the sample
code above in a file named, $HOME/.config/fish/functions/fish_prompt.fish.

I like this convention over configuration style. It’s one less thing for me to
think about. One less decision to make.

In the end, it’s not that other shells can’t do what Fish does. It’s just
that Fish does it by default. Without me thinking about it.

Further Reading

Fish has a Design Document. There are some good ideas there, and it’s worth a read.

If you want some examples of Fish configuration, I’ll offer you my dotfiles repo on GitHub.

The author of Fish, Axel Liljencrantz, wrote an article on LWN.net about
what makes Fish unique. The article is old, but it’s still useful if
you’re an experienced shell user and want an overview of Fish’s features, and
why things are the way they are.