How and why did I switch from zsh to fish

25 Jul, 2022
890 words | 4 min to read | 2 hr, 40 min to write

I was a zsh (and oh-my-zsh) user for many years. And before zsh I’ve used bash for quite a while. Bash was okay, and with a few “add-ons” (oh-my-zsh + a couple plugins) zsh was really good.

But I think if you routinely shopping in only one place, you may be missing out on some amazing deals in other places. So, it worth checking what’s sold in a different supermarket every once in a while. And so I decided to have a look at the alternatives.

Most of the shells did offer different syntax and a few features like Tab-completion, history, arrow-up/down search through previous commands, but nothing that would make me want to switch. Some shells did offer something different, sometimes way too different and I wasn’t adventurous enough for them :)

Fish

Fish was a love from first sight. Right outside of the box it offered elegant syntax and very useful features such as:

  • syntax highlighting
  • history-based completion autosuggestions
  • <Tab> completions
  • man-pages based arguments completion
  • functions, commands, aliases, and even abbreviations completions
  • elegant syntax (better than bash, in my opinion)

Fish stands for Friendly interactive shell, and it really tries to live by it’s name.

zsh

zsh can do more than fish can do with plugins. Plugins list for zsh is just massive. Some of the plugins are inspired by fish’s built-in functions.

But Kirill, if it’s more powerful, why did you switch then?

Plugins are good and bad. On one hand you can add functionality, on the other hand some of those plugins are slowing zsh down. Such plugins as zsh-users/zsh-syntax-highlighting can cause quite significant slowdowns. When I was using zsh zdharma-continuum/fast-syntax-highlighting wasn’t available, so I won’t speculate if it’s better. The point stands still, - you add plugins and can experience performance issues. A few milliseconds here and there add up.

Fish, on the other hand, have almost all I need without add-ons. I’ve already listed some of the main things fish does right out of the box, and they are quite fast!

Other shells

I ruled out shells like tcsh, ksh, and dash, - the look kinda old-fashioned, and the don’t offer anything to be excited about. Should I say anything about sh (Bourne shell) here? I guess, I won’t :) Same goes for the successor of sh, bash (Bourn-again Shell)

Worth checking out

nushell looked very interesting (and still is). It strives displaying information in a better (usually table) form. Also allows querying data better (i.e. you can do something like ps | where cpu > 20 with it). But I guess ability to display information in a table form and ability to query information using SQL-ish syntax are not the selling point for me. I guess because I’m quite good at using find (and fd), grep (and rg), awk and other tools. The shell is super-interesting though, maybe I will give it a try one day.

xonsh is a python-powered shell. It’s a superset of python, so all the python code just works. Also has a pretty long list of plugins (called “xontribs”), so it’s kinda like if zsh was written in python.

nsh is something that I could give a go. I can clearly see fish’s influence, but I also see it’s a little immature and not very well maintained (not sure if it will not share the same fate with rush).

elvish looks neat, I might check it out one day.

Why shell speed matters for me?

Shell performance probably doesn’t matter all that much for the most of the users. If all you do on your shell is running a few commands every once in a while, that’s fine, - 20-100ms delays here and there are tolerable. I personally use shell A LOT, and I can definitely tell when shell performance affecting my own performance.

When I’ve tried fish for the first time, I was blown away by the features I can get without installing any extras, and how fast and fluid it was.

Fishy Caveats

When bash script has no fish version

Some scripts are only written in bash and zsh is a superset of bash and as such can execute any bash script. As such, there is no “native” way of running something like nvm for instance. There is a workaround though. There are execution wrappers such as bax or bass which allow to run bash scripts in fish. So you can still have your nvm available on the command line with a little fish function:

function nvm
if test -f /usr/local/opt/nvm/nvm.sh
bax source /usr/local/opt/nvm/nvm.sh --no-use ';' nvm $argv
end
if test -f ~/.nvm/nvm.sh
bax source ~/.nvm/nvm.sh --no-use ';' nvm $argv
end
end

BTW, I’ve found fnm to be a better (in terms of performance) replacement for nvm.

But most of the time people writing popular software will implement a fish version. For instance, fzf has key-bindings.fish, navi has navi.plugin.fish, phpbrew offers phpbrew.fish.

The point is, this caveat is not really a huge problem.

$_, $?, $$ etc

The special variables which you’d use in bash/zsh aren’t available in fish. I can live without them :)

Rewriting bash scripts to fish

I don’t have an awful lot of scripts, and those which I did have were quite easy to rewrite.