Trying out the fish shell
I’m one of those programmers that does not hold great nostalgia for old programs. I’m always willing to toss aside some part of my software toolkit if something better comes along. This basically means I’m willing to toss any and all UNIX tools I use.
This includes my shell. While I don’t try to make OS X’s GUI do everything, I also don’t think bash/tcsh/zsh are the epitome of UX design either. All of this led me to try switching to the fish shell today. It’s colourful, it can be configured through a browser, and it’s willing to poke fun at itself and shells in general (the tagline on the home page is “Finally, a command line shell for the 90s”; fish wasn’t created until 2005).
But as with all new bits of software, there is a slight learning curve, especially if you have mucked around with your profile in your shell previously. To begin with, fish has scoping rules for its variables. Using the set
command can specify a variable be local, global, or universal. Local is what it sounds like and typically only something you care about when defining a function. Global is for your current shell process. Universal is for all fish shell processes. What this means is that if you are setting environment variables you want global – set -g
– and not local or universal. The key point is to not use a universal scope because those settings will last until the end of time if you don’t unset the variable. This becomes an issue if you are used to your configuration in your .profile
being exactly what you see and not what happens to have been set in the past (e.g. I set the BROWSER
environment variable to a universal scope in my config.fish
file, deleted it from the file, but then forgot to unset it, leading to the environment variable continuing to exist between shell processes until I remembered to unset it manually).
Another thing about variables is that they are not exported by default. That means if you have your shell launch another process – e.g. Python – it won’t pick up any variable you set. I ran into this when I tweaked my PATH
variable and then had building CPython fail because os.environ['PATH']
raised a KeyError
. Turns out I forgot to export PATH
and so it wasn’t made available to the Python process. So unless a variable is truly meant only for your fish terminal you want to export it; set -g -x
is what you want to use for your environment variables.
The other thing that tripped me up quickly was the loss of which
. Turns out that fish has a type
command to handle that sort of thing. Specifically, to directly substitute which
from bash/zsh you want type -p
. Otherwise type
prints out a nicer message which won’t work if you are using type
for its output in another command.
Otherwise things are working out nicely. The niceties included in the completions are such are nice, e.g. the various colour codings. The fish_update_completions
command will parse man pages to generate auto-complete rules. A big perk for me is that fish supports right-side prompts like zsh. This lets me keep my current setup where my left-hand prompt is nothing more than >
while my right-hand prompt is basically pwd
(I have this setup so my commands stay in the same column no matter what, making scanning my command history visually much easier). You can see my config.fish
if you’re curious what my current configuration looks like. Even the shell language is a bit nicer than bash, e.g. every variable is an array of strings, meaning colons as separators are no longer necessary and thus setting PATH
is a little bit more straightforward.
IOW so far, so good.