An intro to fish (not Phish) 🐟

It appeared to me recently that I’ve inadvertently switched to fish shell full time and am loving it! Hopefully after this little intro you will too. For a more complete introduction, I recommend the official tutorial, my aim here is to highlight some of my favorite fish features to entice you…

First, what is fish? Out of the box you get autosuggestions, 24-bit color, man page completions, tab completions, syntax highlighting, and optional web-based configuration. That’s just the tip of the iceberg.

Installation is super easy, for Mac run brew install fish (or grab the pkg!) and Debian-based a sudo aptitude install fish should do the trick.

To start fish, simply type fish in your terminal and you will be greeted with “Welcome to fish, the friendly interactive shell.” If you want to commit right away, we can change our login shell by running chsh -s /usr/local/bin/fish so we always use fish.

The Prompt

“Unlike other shells, there is no prompt variable like PS1. To display your prompt, fish executes a function with the name fish_prompt, and its output is used as the prompt.”

Building a prompt took a little trial and error, but I love that I can script one out in shell vs the PS1 syntax. Running fish_config will launch a browser-based editor, you can set prompts and check the code under ~/.config/fish/functions/ to get ideas, or perhaps one of the defaults suites you just fine, in which case set it and move on.

Here is my current prompt config:

function fish_prompt --description 'Write out the prompt'
  # Save our status
  set -l last_status $status

  set -l last_status_string ""
  if [ $last_status -ne 0 ]
      printf "%s(%d)%s " (set_color red --bold) $last_status (set_color normal)

  set -l color_cwd
  set -l suffix
  switch "$USER"
      case root toor
          if set -q fish_color_cwd_root
              set color_cwd $fish_color_cwd_root
              set color_cwd $fish_color_cwd
          set suffix '#'
      case '*'
          set color_cwd $fish_color_cwd
          set suffix '>'

  echo -n -s (date "+%H:%M:%S") ' ' (set_color normal) (pwd) ' ' (set_color $color_cwd) (prompt_pwd) (set_color normal) "$suffix "

I built upon one of the presets and am only defining the color red for exit status alerts and adjusting the prompt from ‘>’ to ‘#’ when moving between root. Otherwise, I use Solarized Light in iTerm2 so set_color normal retains the Solarized palette, if I wanted to override a color all the time, I could specify a hex value or ANSI name instead of ‘normal.’


“A fish function is a list of commands, which may optionally take arguments. Unlike other shells, arguments are not passed in “numbered variables” like $1, but instead in a single list $argv.”

Say I want to display a notification using osascript, I can test out a function in the terminal like so:

~> function notify
        osascript -e "display notification \"$argv\" with title \"Terminal\""

Now I can call ~> notify Here is an alert! and receive a notification like the following:

I don’t believe you can set a custom icon, at least according to Apple’s documentation unless you were to export a .app version with a custom ICNS file built using iconutil.

This function will disappear after we close out the session, if we want to save it, we can call funcsave notify. Now a folder called ‘functions’ will be created under ~/.config/fish/functions containing a file with the name of our function (in this case ‘notify’) along with a .fish extension. When we
cat the file to peak, we see exactly what we constructed in the terminal.

I love the modular approach to how fish stores functions, instead of running funcsave we can manually create these files in .config/fish/functions. Just remember to name the file the same as the function name with a .fish extension.

Here is another example (a bit arbitrary but maybe this is useful) on manually creating a function, we will call it rf and it will invoke rm -rf against $argv (which would be a path to a folder).

> vim ~/.config/fish/functions/
function rf
    rm -rf $argv

# I can immediately use this
> rf ~/path/to/some/folder/to/delete

Keep in mind, there are some minor nuances when working in fish, for instance a variable assignment:

# in bash
> var=$(pwd)
> name="Drew Diver"

# in fish
> set var (pwd)
> set name Drew Diver


One final and cool fish command is fish_update_completions which parses manual pages installed on the system, and attempts to create completion files in the fish configuration directory. This does not overwrite custom completions.

Ready for more?

Leave a Reply

Your email address will not be published. Required fields are marked *