📰 Good, So So, Bad about Fish Shell

Posted on April 16, 2022 by Myoungjin Jeon
Tags: fish, shell, script, function, util, bash, fisher

Fish Shell Series

  1. Good, So So, Bad about Fish Shell
  2. DRY(Do not Repeat Yourself) in fish

Fish is another shell

I’m too lazy to change my shell. I used to use only bash shell back until 2009?. I heard about tcsh and ksh at that time, but bash was kind of a new standard in linux world.

Maybe zsh rules the shell-world for now, there is another competitor called fish. I didn’t research about fish shell.

However, one thing I really like about fish shell is that fish shell has nice default settings out of box.

Sometimes, even if I installed new fish shell in a new box. I have no problem with using fish shell.

I’m missing some bash history substitution like though:

bash> echo 123
bassh> ^123^456
echo 456

When I found those tricks in a book, I felt like I found some treasure in history. there were more (!$, !!) but I have forgotten many 😉. Frankly, I don’t miss those these days, but I can not deny it is nostalsic.

How About Script in fish?

I heard that fish is not good for making script. But this is not right expression. However, IMHO, bash or dash or zsh? is still better choice for more featrues and portability.

If you think about performance.. I think we need to go for another script languages, for example, perl, ruby, python.. or maybe javascript?!? Perl is the king for portability, but in 2022, python or ruby is available across the unix or linux world, isn’t it?


It has a clean syntax (arguably)

#!/usr/bin/env fish

# find "*.md (or *.markdown)", "*.lhs", "*.org" and print out first 7 lines of
# of each file.

set -l limit_numof_files 5
set -l numof_files_printed 0
set -l interested_extensions 'md' 'markdown' 'lhs' 'org'
set interested_extensions \
    (for e in $interested_extensions
        echo ".$e"

set -l extensions_regex '('(string join "|" $interested_extensions)')$'

for fn in *
    if test $numof_files_printed -ge $limit_numof_files

    if test -f $fn -a \
        test (string match -q -r $extensions_regex $fn)

        set numof_files_printed (math "$numof_files_printed + 1")

        echo -e "$numof_files_printed: filename: $fn\n"
        head -n 7 $fn
        echo -e "\n"


fish shell seems to use plain texts in its syntax, command and control flow. It looks similar to python or ruby. This doesn’t mean it look best but I doesn’t look tricky or geeky at least.

But remember..

You Can Write FORTRAN in any Language.

(This kind of trend will change over and over again, IMHO. And one thing I found that is One day.. someone says he likes the curly brackets in the javascript, which gives him more balanced look. I was suprised because this preference is exactly opposite to what most of people think python has cleaner look than perl back in 200x.)

great default auto-completion

I don’t really have complains about fish’s auto-completion and I don’t think most of unix user never heard about auto-completion. I wouldn’t go deep inside. but you can make your own completions if you want.

powerful builtin functions

documented via man page or website, and also convenient.

For basic calculation, I normally open a terminal and type like below:

fish> math "500+29*50+12*20+210+21*5"

and this is even written in my fish history! So, naturally I could recall it later. (not forever though)

sometimes use the perl to compare the texts against the regex. But now I could finish in fish shell.

remarkable variable scopes

This has pros and cons. It is just because it has different concept. This is where we should read the document very carefully.

However UNIVERSAL variable scope is unique and convenient some times. when you are using several fish sessions and want to share some variable.

But it is easy to make mistake. for example, input set -U somevar in your ~/.config/fish/config.fish. and somevar will be duplicated over and over again.

well organized directory structure

If you have a look into ~/.config/fish/ directory. you will notice that there is a functions directory which consists of functions you can use during the session. It will be automatically loaded when you start a new shell or invoke that function for the first time. (sometimes you need to reload the file by source)

Those function in ~/.config/fish/functions/ are avaiable in you all the sessions.


github repository: https://github.com/jorgebucaran/fisher There might be another kind of programme like fisher out there. Nevertheless fisher is a great third-party tool to make your fish socialized into the internet and install useful themes or tools from the github repositories. I believe that this is quite essesntial concept thesedays.

So So

index starting from 1 not 0

This is a kind of joke 😅, but right now I think this is inconveient for programmer. Because when a programmer is loosing his/her focus and tends to make a mistake when indexing as 0 is very common in programming world.

This is why I don’t think it as better choice, rather I think this is bold movement.

command substitution not as powerful as bash

There is a traditional way to copy but reserve the file attributes. (access time, creation time, ownership, etc) like below:

bash> touch a  b c
bash> mkdir dest
bash> tar cf - a b c | (cd dest; tar xvf -)
bash> (cd dest; ls -l)
-rw-r--r-- 1 myoungjin users 0 Apr 16 21:41 a
-rw-r--r-- 1 myoungjin users 0 Apr 16 21:41 b
-rw-r--r-- 1 myoungjin users 0 Apr 16 21:41 c
bash> ls -l a b c
-rw-r--r-- 1 myoungjin users 0 Apr 16 21:41 a
-rw-r--r-- 1 myoungjin users 0 Apr 16 21:41 b
-rw-r--r-- 1 myoungjin users 0 Apr 16 21:41 c

As you can see above, sometimes grouped command in bash very useful. But, AFAIK, fish cannot do this or prevent this kind of behaviour.

update 20th Apr 2022

To correct my mention above, I’ll show more example. It is not impossible but still I think it is not as intuitive as bash way.

This is a equivalent solution:

fish> tar cf - a b c | fish -c "cd dest; tar xvf -"

Or this is a another approach:

fish> tar cf - a b c | begin cd dest; tar xvf -; cd -; end

The difference between two codes is that the former code used sub-shell1 as bash had, the latter one was still in the same level of your current fish session.

In either way, I could tell bash has more intuitive syntax for sub-shells.


Unusual variable scope

Actually, this is very powerful concept in fish, however it is not very common, In other words, it is confusing. This confusion occurs highly when you are dealing about local scope variable which is created by set -l varname.

local scope is very limited scope which is not even propagate into the a local scoped function(declared in your script). So if you want to make a function, we need to aware about how we deliver some values from local scoped variables.

Very well.. this maybe so you might need to study before making a function or script in fish. Otherwise you will get headache because generally shell script doesn’t consult you kindly about your typo or misconcept.

Oh, This shell makes too lazy (Bad??)

Nevertheless, fish is a great shell which I love to use everyday. And I lost chance to use zsh. This is not about mannerism. 😂 Just because of fish shell is enough for me.

Thank you for reading!


  1. Bash Sub Shells: https://www.linuxjournal.com/content/bash-sub-shells↩︎