After over a decade of using Bash and almost a decade of using Zsh I decided to take Fish for a spin.

My reason for doing so was that even though Z-Shell is very powerful, its learning curve is (too) steep and the defaults are incredibly Spartan. Sometimes I get the feeling that while I know that to scratch my itch, there is an option or tweak in Zsh, at the same time I simply cannot be bothered to find and implement it – the relative effort is just too big. This holds true even after I (reluctantly) started using Oh My ZSH! to more easily manage my scripts.

Fish has been around for over a decade now and was created with user-friendliness and interactivity in mind (hence the name!). Another benefit that I found is that it comes with all its great features enabled by default.

Its major down-side is that it breaks the POSIX standard in certain aspects and is therefore not 100% compatible1 with other (POSIX) shells like Bash and Zsh. Fish claims that it breaks the standard only where and when it makes sense2.

With this series of posts, I do not intend to convince anyone to use Fish, but simply present my experience as I learn. It might just as well be that in the end of this excursion I will not make the switch.

Because getting used to a new environment – such as desktop or a shell – takes its time, I plan to write about it as a some sort of a diary. This, first, post speaks about the very first few days that I spent with Fish, the next one will explain my experiences after a few weeks, followed (hopefully) by one to provide an overview of some months of use.

Day 1 – Getting to know each other

Read the oldest article on Fish on LWN where Axel Liljencrantz, the original author of Fish, introduces (t)his new shell. Great and concise read!

Installed Fish on my Mageia laptop – easy peasy.

First impression:

  • the defaults seem nice and full-featured;
  • it is odd to not see my own familiar Zsh prompt any more, but the Fish default is not bad;
  • love Fish’s tab completion – I might need some more getting used to, but it is very neat and powerful;
  • its syntax error highlighting and wild-card expansion previews are amazing;
  • ditto for the tab completion when searching for man pages;
  • help command is great and even context-aware, but I am not sure yet if I like that it opens documentation in the browser;
  • open command is very useful if you cannot remember which programme you need to open a specific file (or are simply lazy) – e.g. open my_doc.pdf on my KDE desktop will automatically open the PDF up in Okular.

The thing I miss most on my Zsh prompt is that I wrote it to be VCS-aware. Luckily a quick search showed that (at least for Git) this is very much possible to do. So far the two most promising HowTos seem to be the one by Mariusz Smykuła and an older one by Martin Klepsch.

I just stumbled across Oh My Fish! – the Fish equivalent of Oh My ZSH! Since I am not a fan of huge bundles of plugins and like knowing what my scripts do, I will try to avoid it for as long as possible, but I thought it worth mentioning here.

Surprise of the day – fish_config

I found the following command by monkeying around and pressing tab on random strings to see how it works.

➤ fish_config

To my huge surprise it opens up a page in the web browser where you can view and edit settings of your Fish. It even includes prompt theme previews and a colour picker. Wow … simply wow!

For now I only played around with it a bit and just changed the prompt.

At this stage, I am already considering making trying Fish as a default shell (at least for a while). Maybe tomorrow, we shall see …

Day 2 – Climbing the easy learning curve

Started reading the tutorial – basically everything that I stumbled upon so far is covered there. Plus tonnes more of course! I found it just the right length for a tutorial – not too short, not too long.

The tab completion is great. I found some occasions where it beat Zsh, but also a few where it is not on par with Zsh yet. At the moment I would call it a tie.

There is a difference between using single and double quotes (as in other shells), and the difference between them is apparent as soon as you type a variable. Namely the colour of the $ sign in front of the variable remains the same as the rest of the string if it is simply used literally (in single quotes); and changes to contrast the rest of the string, if the variable in the string will be substituted/expanded for its value.

Yesterday I complained about tab completion missing for a few commands that I use. Today I found out about the fish_update_completions command, which does exactly what you would expect – it scans the manpages for commands and creates tab completion patterns for them.

I tried to set Fish as a my (normal user’s) default shell on Mageia, but could not find the option in its official settings GUI and when I tried to set it via chsh I was presented with the following error:

chsh: "/usr/bin/fish" is not listed in /etc/shells.
Use chsh -l to see list.

There is a bug report for this already upstream, but it considers it as a downstream/packaging bug. Consequently, I opened up a bug report on Mageia’s bugzilla.

Day 3 – Setting Fish as default

Even after Fish scanned the man pages, I noticed that for a few commands it still does not provide tab completion. Luckily it seems that this is programmable and I can add what I need later (and maybe even send it upstream).

Making Fish my default on Mageia

The bug I opened up yesterday, was immediately fixed3 by the Mageia community – great job!

Before that I already manually added /usr/bin/fish to /etc/shells and made it my default. I like it enough already to try it for real. And if I get fed up, I can just make Zsh my default again.

Caveat: The following command that you will find in Fish’s tutorial will not work on Mageia and does not for other shells either:

chsh -s `which fish`

This is because which (on Mageia) finds the shells in /usr/bin/, while in /etc/shells they are stored with their /bin/ paths. This seems to caused by /bin being just a symlink to /usr/bin.

The correct CLI way (on Mageia) is therefore:

hook at hermes on pts/3 in ~
{ # }: cat /etc/shells
hook at hermes on pts/3 in ~
{ # }: chsh -s /bin/fish

Making Fish my default on Jolla SailfishOS

In order to use Fish under Jolla’s SailfishOS, you need to install both Fish and Man-DB from the OpenRepos/Warehouse. Man-DB is not strictly necessary, but if you do not install it, you will miss useful hints and some tab completion.

After that you can simply run:

[nemo@Jolla ~]$ chsh -s /usr/local/bin/fish

… and enter your phone’s dev/admin/root password.

Do not forget to (re)run fish_update_completions to update the tab completion.

Day 4 – Serious use of Fish

Almost entirely got used to the tab completion and arrow key history. It is pretty intuitive. I thought I would miss the Z-Shell in this regard, but I really do not. There are a few commands still missing, but these seem to be easy to generate or write by hand.


Started migrating some aliases from my Zsh dotfiles into Fish functions and found it very simple and intuitive using function (in-line functions creator), funcsave (save a function, if you like it for future use) and funced (in-line saved functions editor). The fact that you do not have to dive into a separate directory and open a text editor, but can simply create a function where you are as soon as you need one, actually makes quite the difference in the user experience.

Already created a few scripts/aliases with function and funcsave:

function ttlog --description 'Shortcut for editing GTimeLog'
  vim ~/.gtimelog/timelog.txt
function tree --description 'Tree with colours and (by default) just two levels deep'
        command tree -C -L 2 $argv

The scripting syntax in Fish is different from most other popular shells, but I found it very intuitive to write new scripts and aliases. But …

Disappointment of the day – scripting GnuPG in Fish

In contrast, I did not manage to migrate the following one though due to the fact that gpg-agent --daemon --enable-ssh-support produces an output that includes variables in the form of VARIABLE=content, which Fish fails to parse.

pkill gpg-agent
if [ -x /usr/bin/gpg-agent ]; then
        eval $(/usr/bin/gpg-agent --daemon --enable-ssh-support)

I am sure this is solvable – if nothing else through the use of sed, but for the time being I am OK with simply keeping that script separate.

Still, for the first few days, I am very happy with how this trial is progressing and am looking forward to how it will continue in the next weeks ☺

hook out → here fishy fishy fishy…

  1. Well, truth be told, even Bash is not 100% POSIX compliant out of the box, but can be very easily configured to be. 

  2. It is not alone in the stance that it might slowly be time to break dated standards where it makes sense and hopefully replace it with more modern ones. Of notable examples with a similar idea Nix & NixOS pop to mind, as well as Lennart Poettering. 

  3. Kudos to Jani “wally_” Välimaa

Related Posts

Reading Time

~7 min read


Trying out Fish




Stay in Touch