Fish shell

from Aral Balkan

Jul 25, 2021, 12:27:53 PM
An ASCII drawing of a fish

Fishing for a new shell? (Sorry.)

Beautiful defaults

Good design isn’t about removing the seams altogether1; it’s about having beautiful defaults and layering the seams. And this is something that Fish shell absolutely nails.

To be fair, I have no idea why I didn’t give it a proper shot until yesterday. I guess because Zsh, or more precisely Oh My Zsh, has been perfectly adequate.

It’s not until you experience the intelligent auto-suggestions and completions in Fish, however, that you truly realise what you’ve been missing. And that’s before you realise just how easy it is to extend as nearly everything is a function.

Getting started

First off, download and install Fish shell.

I’m running elementary OS on my Star Labs notebook so I followed the Ubuntu instructions:

sudo apt-add-repository ppa:fish-shell/release-3
sudo apt update
sudo apt install fish

At this point, you can simply type fish in your current shell to open a Fish shell instance.

Out of the box, you will get the excellent auto-suggest/completion.

I recommend you take a moment to at least skim through the excellent tutorial, documentation, and frequently-asked questions.

Sprucing it up

While Fish shell is perfectly usable without customisations, there a few things I’ve gotten used to having in my shells that I wouldn’t want to do without. These range from the practical to the merely cosmetic.

The first thing I would highly recommend you do is to install Fisher.

Fisher

Fisher is a plugin manager for Fish. It does one thing and does it well.

You can install it by running the following one-liner (this is the script that it will run on your machine).

curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher

From there, you can browse the list of Fisher plugins on awsm.fish.

Following is a brief list of the ones I installed.

Tide

Tide is a modern prompt manager for Fish.

After playing with it a little, I forked it to customise the Git prompt (I like to have my Git prompt tell me what the status is in words instead of cryptic symbols.)

Screenshot of my prompt. It reads: ~/ar.al/site ❨git❩  master 1 untracked

A word is worth a thousand symbols. Or something.

Install using Fisher:

fisher install IlanCosman/tide

Gills

Screenshot of Gills showing the empty line after the command and the command output

Fish shell gives you gills (I need help, I know.)

Making a plugin is so simple in Fish (it’s just a function) that I’ve already made my own (very simple one) even though I only just started using it yesterday.

The plugin, which I call Gills, adds an empty line after your prompt and before the output of your command to balance the whitespace around them so you can more easily separate your prompts from your command output while skimming your terminal’s scrollback buffer.

It’s called Gills because it gives your output room to breathe. Get it? (Did I mention I was sorry?)

Technically, like basically everything else in Fish shell, it’s just a function that handles Fish shell’s fish_postexec event.

(It also handles a couple of special cases like cd, pushd, and popd that do not produce any output. In those cases, it doesn’t add the additional empty line. If you find any other edge cases, please feel free to open an issue or a pull request.)

Install using Fisher:

fisher install small-tech/gills

Node Vesion Manager (nvm)

I work with Node.js everyday so, prior to Fish shell, I was using nvm-sh/nvm to manage my node versions.

While you can still use that script in Fish, there is a better option that’s called nvm.fish.

Designed for Fish, this tool helps you manage multiple active versions of Node on a single local environment. Quickly install and switch between runtimes without cluttering your home directory or breaking system-wide scripts.

Install using Fisher:

fisher install jorgebucaran/nvm.fish

From there on, you can use commands like nvm install lts to quickly install and use different versions of Node.

nvm.fish also supports .node-version and .nvmrc files so pop one of those bad boys into your project directory to automatically have the Node version specified within available when you switch to the directory in your shell.

Puffer Fish

One feature of Zsh that I use all the time is to navigate back multiple directories by simply adding more dots to my command. So, for example, if I’m in the /a/b/c/d/e/ directory and I want to change to the b folder, I can simply write cd .... and Zsh will interpret that as cd ../../../ and save me some typing.

(Hey, I’m a developer, being lazy is part of the job description.)

So, anyway Puffer Fish lets Fish shell do the same thing. Actually, it’s even better because you get in-place expansion.

Install using Fisher:

https://github.com/nickeb96/puffer-fish

Autopair

Autopair automatically matches opening/closing pairs of common punctuation like quotation marks and parentheses.

It might seem like a small thing, but it’s one of those things that gives you a little jolt of dopamine every time it Just Works™.

Install it using Fisher:

fisher install jorgebucaran/autopair.fish

Dracula theme

I like and use the Dracula theme pretty much everywhere and, of course, there is a version for Fish.

Install it using Fisher:

fisher install dracula/fish

Configuration

Screenshot of the web interface for fish_config, showing the  colors, prompt, functions, variables, history, bindings, and abbreviations tabs.

How do you like your Fish?

If you’re coming from Bash or, like me, from Zsh, you might be wondering where to put your aliases, etc. The equivalent of ~/.bashrc or ~/.zshrc in Fish is ~/.config/fish/config.fish.

You can also configure your shell visually in your web browser by running the following command in your terminal:

fish_config

Also, while Fish does have aliases, it also has a much more powerful alternative called abbreviations

Abbreviations

Screenshot of Fish shell suggesting abbreviation completions for the command get-lo: git-log (Abbreviation: git log --graph --decorate --pretty=oneline --abbrev-commit) and git-log-dates  (Abbreviation: git log --graph --decorate --pretty=format:

Abbreviations? DMIID (Abbreviation: don’t mind if I do)

Abbreviations are just regular Fish functions but you get a handy shortcut (abbr) for defining them. Unlike aliases, you get auto-suggestions for abbreviations and they are expanded in place so you can see the full command.

While I’ve converted most of my Zsh aliases to abbreviations, I’ve kept some (like code for codium) as aliases.

My advice is to convert all your aliases to abbreviations and then you will know right away whether you need to convert any back to aliases when you use them. (e.g., I didn’t want to keep seeing /usr/local/codium on my history so I decided to keep code as an alias.)

Here is my current config.fish file, in case it gives you some ideas. I’ve also listed links to the custom commands used to make it easier for you to find and install them.

if status is-interactive
    #
    # Aliases
    #

    # VSCodium
    alias code '/usr/bin/codium'

    #
    # Abbreviations
    #

    # Copy and paste to the clipboard by piping to these commands.
    # (Inspired by the default behaviour in macOS.)
    abbr --add --global pbcopy 'xsel --clipboard --input'
    abbr --add --global pbpaste 'xsel --clipboard --output'

    # Open files in their associated apps from Terminal.
    abbr --add --global open 'xdg-open &>/dev/null '

    # Git aliases to make git a bit more humane for everyday use.
    abbr --add --global git-log 'git log --graph --decorate --pretty=oneline --abbrev-commit'
    abbr --add --global git-log-dates 'git log --graph --decorate --pretty=format:"%h [%cr] %s'
    abbr --add --global git-tag 'git tag -n'
    abbr --add --global git-undo-last-commit 'git reset HEAD~'

    # Aliases for getting system and app information.
    abbr --add --global system-information 'neofetch'
    abbr --add --global disk-usage 'dust'
    abbr --add --global which-kernel 'apt-cache policy linux-generic'
    abbr --add --global node-v8-version 'node -p process.versions.v8'

    # Make rm a little safer (have it prompt once when deleting
    # more than three files or when deleting recursively).
    abbr --add --global rm 'rm -I'

    # A nicer ls that also shows the git status of files
    abbr --add --global l 'exa -lh --git --all'

    # A nicer ls that also shows the git status of files
    # (but not hidden files)
    abbr --add --global ll 'exa -lh --git'

    # Same nicer ls but in tree view.
    abbr --add --global lt 'exa -lh --git --all --tree'

    # lc = line count
    abbr --add --global lc 'wc -l'

    # Find out what’s running on port X
    abbr --add --global port 'lsof -i'

    # Better find
    abbr --add --global find 'fd'

    # Better ps
    abbr --add --global ps 'procs'

    # Package.json validator
    abbr --add --global validate-package.json 'pjv package.json'

    # Use ripgrep instead of grep
    abbr --add --global grep 'rg'
end

Tools mentioned in the configuration

  • XSel is a command-line program for getting and setting the contents of the X selection. Normally this is only accessible by manually highlighting information and pasting it with the middle mouse button.

  • xdg-open opens a file or URL in the person’s preferred application.

  • neofetch displays information about your operating system, software and hardware in an aesthetic and visually pleasing way.

  • dust is a more intuitive du for visualising disk usage.

  • exa is a modern replacement for ls that uses colours to distinguish file types and metadata and knows about symlinks, extended attributes, and Git.

  • fd is a simple, fast and user-friendly alternative to the find command for finding entries in your filesystem.

  • procs is a replacement for ps in a human-readable format and with some other additional features.

  • ripgrep (rg) is a line-oriented search tool that recursively searches the current directory for a regex pattern. It is similar to other popular search tools like The Silver Searcher, ack and grep.

  • package.json validator (pjv) verifies that your package.json files are correct.

Next steps

This introductory post doesn’t even begin to scratch the surface of what you can do with Fish but I hope that it sparks your interest and whets your appetite.

To learn more about it, view the documentation on the Fish shell website. You can also launch the documentation in a browser locally from Fish shell itself using the help command.

For example, to learn about abbreviations, run the following command:

help abbreviations

Have fun and remember: There might be plenty of other shells in the ocean, but only this one is a fish. (Again, I’m really sorry, I don’t know why I’m like this either.)

Like this? Fund us!

Small Technology Foundation is a tiny, independent not-for-profit.

We exist in part thanks to patronage by people like you. If you share our vision and want to support our work, please become a patron or donate to us today and help us continue to exist.


  1. If you remove the seams altogether, you end up with the shiny locked boxes that Apple ships. Sure, they’re lovely to look at and use but you do so on the ever-evolving unilateral terms imposed by the corporation that makes them. This has considerable ramifications when it comes to protecting your freedom to own, control, and use your tools as you want to and impacts on your human rights (e.g., privacy), right to tinker, right to repair, etc. ↩︎