Continuing in the vein of my previous posts about changes I’m making to my development setup, this week I have been looking at shells. Somewhere once upon a time I encountered the idea that cool kids don’t use bash, and the commonly-cited alternatives are zsh and fish. A thread on Reddit pointed out a few of the compelling advantages of zsh over bash, and Ars Technica has a fairly old article detailing some of the ideas behind fish.
Some people complain about fish’s not supporting !! and related syntax like !$, but I’ve always found the expansion of ! problematic (when e.g. writing commit messages) and I prefer to just use interactive editing of commands from my history, or Alt-. to retrieve the last word in a previous command. (fish supports both quite well.)
Some features that I really like about fish so far are:
- Tab completion. I think I must have tried zsh once a long time ago because I have some sort of vestigal memory of trying to tab-complete some filename and hating it. Specifically, I remember hitting Tab a few times (which is deeply ingrained in my muscle memory from years of using bash), and zsh immediately inserting something into my command line, with my only option to keep pressing Tab until I got to what I wanted. (I believe this is called menu completion in the literature.) I felt this was too aggressive; if I wanted to refine the filename a bit, or if I saw that I had entered the wrong directory, I had to erase what the shell had just injected. fish’s tab completion is like all the best parts of this mechanism, but better. It isn’t adequately covered in the documentation, but this Stack Overflow post does a good job detailing its behavior. Basically, you can cancel the tab completion using Escape, and you can refine the search by typing stuff even once you are in "menu" mode, sort of like an ido/swiper sort of thing. Very slick.
- If a command prints some output that doesn’t end in a newline before you get to a prompt, fish puts out a cute ⏎ symbol and inserts a newline. Your prompt always starts on a new line, but you get a clear indication that what you see isn’t exactly what you got.
- Ctrl-K and Ctrl-Y on the fish command line interoperate seamlessly with my windowing system’s clipboard.
- I’ve never been super into pushd/popd, but fish has prevd and nextd which seem pretty nice.
Lots of people online recommend oh-my-zsh or zprezto, but it mostly seems like the things zsh offers (even with these packages) aren’t incredibly amazing. Maybe there’s some draw for die-hards, but there’s a lot of overhead involved in learning these packages, setting them up, and maintaining them. By comparison, fish has hardly any configuration available at all, so hopefully you like how it works, because if you don’t, you can’t change it.
In general, the dichotomy between zsh and fish is between the incredibly flexible Swiss-Army-knife all-singing-all-dancing tool with a gazillion options, and the beautifully crafted tool with only one, extremely-carefully-thought out option. zsh is KDE and fish is GNOME. Or, zsh is Perl, and fish is Python. I’m sure you can come up with some other analogies.
I’m still getting my prompt and favorite functions set up, but a few commands I’ve found useful:
- type [commandname] lets you see what kind of command something is, and if it’s a shell function, see its definition. Apparently this is common to even bash, but I had never used it before.
- vared lets you interactively edit a variable. zsh has this too; fish also has funced for editing functions.
A couple things I’m annoyed about:
- The fish web page compares fish’s autocomplete behavior to a browser’s, but there’s no way to tell fish to not record history for a shell session, analogous to a browser’s "private browsing" mode.
- In both fish and zsh, array indices start at 1, not 0 as you might be used to.
- fish’s readline doesn’t support Control-_ for undo, and maybe doesn’t support undo at all.