At the SoCraTes conference I attended a demo of the fish shell and was so impressed that I pledged to use fish
instead of zsh
as my main shell for 4 weeks. This is the result of the experiment and contains my collected experiences.
Fish is awesome
I really like fish
and will continue to use it. The syntax highlighting of the the commands gives immediate feedback (unknown commands are shown in red) and the command autosuggestions are a real time saver. There are even more features I like:
- Word navigation with
Alt+Left
andAlt+Right
. - Very pretty, informative prompts, with easy definition of “right prompt”.
- Prompt definitions are readable functions and not a jumble of escape characters
- Configuration lives in a directory (
~/.config/fish
) instead of one single file. It is modular and can stretch multiple files, making it easy to keep it under source control.
Stumbling blocks
Not everything was awesome from the start. Indeed, the transition from a souped-up zsh
environment to fish
was very painful, because I made one mistake: Using the budspencer theme. It was not the fault of the theme, but entirely the fault of my lust for shiny features. budspencer
was the theme I saw at SoCraTes, it was so pretty and its feature description so enticing I immediately installed it. However, it has one trait that got in the way: vi mode. Vim is my favorite editor, so it seemed only natural to fully embrace the familiar keyboard navigation when editing commands. But what sounded great in theory collided with 20+ years of muscle memory and shell knowledge: I’m always moving the cursor with Ctrl+A
and Ctrl+E
, I’m used to getting a nice, searchable history with Ctrl+R
, I like to type sudo !!
, cd -
and frequently use the $!
variable. fish
supports these things, but the budspencer
theme interfered too much with the default behavior of the shell, especially by enforcing vi key bindings that were incompatible with the key bindings of the plugins. Lesson learned: Start with the default configuration, add features slowly and only when you really need them and understand how they work!
The other hurdle was my accumulated zsh
history which contains long commands that I use almost weekly but that are too long to remember. There is a node.js script to import zsh
history into fish
, but my zsh
history also contains thousands of worthless cd
, ls
and less
commands I did not want to import. This was the moment where I wrote my first fish
function:
Searching the zsh
history from fish
I already installed the “command line fuzzy finder” fzf
for accessing my fish
history with Ctrl+R
. The fzf
command takes multiple lines of text as input and offers a search interface where you can search through the whole text and use the cursor keys to select one line which is printed to stdout when pressing enter. My function converts the zsh
history file format (consisting of timestamps and commands, separated by semicolon), pipe it to fzf
and then create a new command line buffer with the selected line. Looking at the code for the fish
integration I came up with a configurable function:
function zsh_history -d "Search through the zsh history with fzf"
set -q ZSH_HISTFILE; or set ZSH_HISTFILE ~/.zsh_history
set -q FZF_ZSH_HISTORY_SEARCH; or set FZF_ZSH_HISTORY_SEARCH --no-sort
# remove duplicate history entries with awk and only output commands
awk -F\; '{ if( a[$2] == 0) print $2; a[$2]++ }' $ZSH_HISTFILE | fzf $FZF_ZSH_HISTORY_SEARCH | read -l result
and commandline -- $result
end
The awk
command is not perfect because it will mangle multiline commands, but overall the function works very well. It’s storing every command as a key in the hash a
, counting up its value whenever it encounters the command and only printing the command when the counter is zero. Suggestions for improving the parsing of multiline commands are welcome.
In the future I’ll try to bind the function to Alt+Z
(or rather Esc+Z
on my Mac where Esc
takes the role of the Alt
key by default).
My current setup
I’m using oh-my-fish as a “package manager” for plugins and themes, but at the moment I’m only using the
bang-bang
plugin for repeating the last argument/command. I use bobthefish
as my theme. Using such a fancy prompt needs to have special fonts in macOS Terminal. I have replaced the default “Menlo” font with “Meslo LG S DZ Regular for Powerline” from the Powerline fonts repository. “LG S” is for “small line gap”, resembling the original Terminal font as closely as possible.
I’ve configured my Terminal to accept Alt+Up
keystrokes to insert the last argument. With this setting I will probably use the bang-bang
plugin less and less.
I decided to go with oh-my-fish
because I already has good experiences with oh-my-zsh
. But oh-my-fish
is more of a “spiritual port” than a 1:1 clone of oh-my-zsh
. oh-my-fish
even has a handy CLI tool to manage themes and plugins where oh-my-zsh
solely relies on editing configuration files.
Some minor pain points
- I have to get used to using
(somecommand)
instead of$(somecommand)
or`somecommand`
. Especially when copying Docker commands from the web or from colleagues, it’s(pwd)
instead of$(pwd)
. - I still have to fully understand key bindings, especially when needing to distinguish between vi mode and “normal mode”.
- I have to memorize how to disable vi mode:
fish_default_key_bindings