The fish shell

Published: January 31, 2015

I've been using zsh with a few customizations for quite a while now and I can honestly say that I would never go back to just using plain bash (although that is not entirely true: I do my scripting in bash, since bash is the common denominator amongst many *nix systems).

Some of the things that I liked about zsh include better tab completion and interactive syntax highlighting. I also configured my prompt to show the current status of a git repository, if the current directory contains one. Some of these customizations felt like a bunch of hacks, but they worked just fine.

Today I stumbled across the fish shell, which, unlike zsh, breaks backward compatibility to bash, but advertises itself as a "smart and user-friendly command line shell for OS X, Linux, and the rest of the family."

fish.png
Figure 1: my fish setup

So, curious as I am, I installed it and played around with it. I really liked my custom zsh prompt, so one of the first things I tried to do was to recreate it in fish. Unlike in bash or zsh, the prompt is not set by a P$1 or PROMPT variable, but rather by executing a pair of functions called fish_prompt and fish_right_prompt. Both of these functions lay in separate files in the directory ~/.config/fish/functions, and in case they are being changed, the shell always executes the latest version. Neat!

One of the things that sold me on fish was the nice and pain-free syntax used for configuration and scripting. For example, look at this snippet from my .zshrc, where I configure my prompt:

spc_cnt=$(hostname | wc -m)
let "spc_cnt-=1"
spaces=$(printf ' %.0s' {1..$spc_cnt})

ZLE_RPROMPT_INDENT=0
autoload -U promptinit
promptinit
source ~/.git_prompt.zsh
hostname="%{$fg[blue]%}%m%{$fg[red]%}%B|%b"
return_indicator="%B%(?,%{$fg[green]%}✓%{$reset_color%},%{$fg[red]%}:(%{$reset_color%}%b"
directory_indicator="%{$fg[yellow]%}%~"
prompt_last="$spaces%{$fg[red]%}%B|%b%{$reset_color%}▸ "
_newline=$'\n'
PROMPT=$_newline$hostname$directory_indicator$_newline$prompt_last
RPROMPT=$RPROMPT$return_indicator

A little chaotic, right? In contrast, here are my fish_prompt and fish_right_prompt functions:

function fish_prompt --description 'Write out the prompt'
  set -l spc_cnt (math (hostname | wc -m) - 1)
  set_color blue
  printf "\n"
  printf (hostname)
  set_color red -o
  printf "|"
  set_color yellow
  printf (prompt_pwd_dennis); printf "\n"
  for x in (seq $spc_cnt); printf " "; end
  set_color red -o
  printf "|"
  set_color normal
  printf "▸ "
end
function fish_right_prompt
  set -l last_status $status
  set_color normal
  printf '%s ' (__fish_git_prompt)
  if test $last_status -eq 0
    set_color green
    printf "✓"
  else
    set_color red
    printf ":("
  end
end

Note that fish has a built-in git status prompt, so no more hacks to get that functionality in zsh!

The final thing that sold me on fish was the great tab completion feature: When I enter a command, fish prints its suggestions in a light grey as I'm typing in my command. Together with its equally great syntax highlighting, fish is just a pleasure to use!

The picture at the top of this post illustrates the tab completion and also shows my prompt, together with my git status indicator. I will keep using fish as my everyday shell for now and I will blog about some more tricks that I pick up along the way.

Best, Dennis