# fish: just a little bit nicer


I’ve been trying [`fish`][], however briefly, for a few years now. This time, I finally stuck with it. If you’re interested in seeing what the initial adjustment period is like, have a look at [fish: the inital adjustment period][adj].

[adj]: ../fish-initial-adjustment/
[`fish`]: https://fishshell.com/

Previously, I’d try `fish` only to discover that it didn’t support changing the title of Terminal.app or that `fish` sessions wouldn’t restore properly[^1]. By `fish` 3.0.2, both of these problems were fixed and I was able to give `fish` a fair try.

After a bit I ended up liking it. `fish` bills itself as “the friendly interactive shell”. I’d describe `fish` as “just a little bit nicer”.

You can browse the [`fish`][] site to get an idea of some of its feature set and what makes it better than other shells. This page lists some of the reasons why **I** like `fish` more than `bash` 3.2.57, the version that ships with macOS 10.14:

## Configuration goes in `~/.config/fish/`, making it easier to version-control

`bash` configuration tends to be sprawled at the top level of one’s home directory. In order to properly version-control `bash` dotfiles, one needs to either:

1. put them in a version-controlled directory elsewhere and symlink to them from `~`
2. use a version-control system that doesn’t try to version-control everything in `~`, like [src][].

[src]: http://www.catb.org/esr/src/

I like `src`, but I can’t use it to sync changes between my desktop and laptop through a service like GitHub. `~/.config/fish/`, however, is easily synced with a `git push` on one machine and a `git pull` on the other.

## No profile/rc split

`bash` has both `~/.bashrc` and `~/.bash_profile`. I never really did understand when each gets loaded up. When I was first bitten by this split, I decided to write both such that they both sourced `~/.globalbashrc` and that was the end of it. Later on, as I moved to Macs mostly full-time and stopped bouncing between terminals both inside and outside the X Window system, I got less defensive with my `bash` configuration and moved to sourcing `~/.bashrc` from my `~/.bash_profile`, although I couldn’t tell you why I did it this way and not the other way. At any rate, I have my `$PS1`-prompt twiddling and alias setup in `~/.bashrc` and my `$PATH` prepending in `~/.bash_profile`, although I couldn’t tell you why.

`fish`, mercifully, is much simpler, or at least it’s easier to understand where to put things.

- Settings that `fish` likes setting itself (colors, etc.) go in `~/.config/fish/fish_variables`. (The convenience of [universal variables][] is nice, but reading `fish_variables` with its `\x1e` and similar escapes is a bit of a chore for long variables.)
- `~/.config/fish/functions/` for, well, all functions. One file per function. (fish uses functions for the sorts of things one would use an alias for in `bash`.)
- `~/.config/fish/config.fish` for everything else, including building up `$PATH` and `$EXA_COLORS` over multiple lines apiece.

[universal variables]: https://fishshell.com/docs/current/tutorial.html#universal-variables

## `?` doesn’t glob

This is a small thing, but dagnabbit, I like it.

I use [youtube-dl](https://ytdl-org.github.io/youtube-dl/) a fair amount. If I’m downloading a URL like `https://www.youtube.com/watch?v=dQw4w9WgXcQ`, I need to quote it with single quotes in `bash`. I don’t need to bother typing the quotes in `fish` if I’ve done `set -U fish_features stderr-nocaret qmark-noglob` first.

Why also `stderr-nocaret`? It disables an old annoyance that I ran into. `^` isn’t anything special in `bash`, but it is in `fish`. `git diff HEAD^ HEAD` works just fine in `bash`, but you’ll want to enable `stderr-nocaret` in `fish` to keep git and regex annoyances to a minimum.

[^1]: Frequently, I have four different Terminal windows open to four different paths. Back when `fish` didn’t support session restoration, each terminal would get reset to my home directory instead of wherever its current working directory was before I quit Terminal. This was more than enough to switch back to `bash`.

## Tab-completable makefile targets

I use simple [makefiles][make] in most of my projects. Not only do makefiles reduce the amount of typing you have to do, but they’re a useful memory aid when you return to a project after a spell and wonder “how do I make it go, again?”. Previously, in order to view makefile targets, I’d have to `cat` the file to see what targets were in the makefile. With `fish`, though, all I need to do is type `make`, a space, and then hit the tab-key twice.

[make]: https://en.wikipedia.org/wiki/Make_(software)

## More-accessible history

This is the feature that I end up using the most, it seems.

`fish` history autocompletes. Press → or C-f to complete the current suggestion. It’s a bit weird to see your old Git commit messages autocomplete when you type `git commit -am`, but seeing `cd Projects/Go/src` after just typing `cd` gets handy. I wouldn’t bother searching through my history and typing `!234` for something like that.

If you want to keep a few commands out of your history, `fish -P` [starts `fish` in private mode][privmode]. I’ve used this for a screencast or two.

---

If these features sound neat, head on over to [the fish website][`fish`] to find out more. If you’re wondering whether switching is a pain (it was for me, if only for a few days), have a look at [my page on the initial adjustment period][adj].

[privmode]: https://fishshell.com/docs/current/interactive.html#private-mode

