➜ sudo apt-add-repository ppa:fish-shell/release-3
I’ve finally made the switch from ZSH to the fish shell. Unlike ZSH, I don’t need to install and manage a whole host of plugins. Everything I want comes packaged right in as part of the shell itself.
In this post, I describe how to switch to the fish shell and some of the interesting features it offers.
Installing fish is a piece of cake. On Ubuntu, a fish package is readily available. An additional PPA supplies newer versions of fish than those that ship with the distribution. The following steps install fish through the PPA.
Once installed, switching is just a matter of updating the login shell.
/usr/bin/fish as a login shell by adding its path to
Debian provides a convenient
add-shell command to accomplish this.
➜ sudo add-shell /usr/bin/fish
Set the current user’s login shell to fish with
➜ chsh -s /usr/bin/fish Password:
Now, open up a fresh terminal session to start using fish.
You should see a fresh, vanilla shell prompt like the following.
Welcome to fish, the friendly interactive shell Type help for instructions on how to use fish jordan@jwillkers ~>
Adjusting basic configuration options is quick ang easy with fish.
Configuring the fish shell is as easy as selecting options the provided by the web interface.
Style the prompt.
If you like that oh-my-zsh feel as I do, you might want to use robbyrussell’s theme for your shell prompt. If you like doing things the old fashion way - or want to set the prompt remotely, here’s a quick rundown.
Make sure the
~/.config/fish/functions directory exists.
➜ mkdir -p ~/.config/fish/functions
Then copy over the desired prompt configuration,
robbyrussell.fish in this case, from
➜ cp /usr/share/fish/tools/web_config/sample_prompts/robbyrussell.fish ~/.config/fish/functions/fish_prompt.fish
Adjust the color theme.
The color theme can be important when your terminal’s color theme obscures autosuggestions.
Solarized is my color scheme of choice.
fish_config web tool outputs the commands that set the various variables for a color theme when one is selected.
This is easy to translate to a command-line which can be quite helpful when configuring remotely.
As an example, the following command sets the color scheme to solarized dark.
➜ set -U fish_color_normal normal; \ set -U fish_color_command 93a1a1; \ set -U fish_color_quote 657b83; \ set -U fish_color_redirection 6c71c4; \ set -U fish_color_error dc322f; \ set -U fish_color_end 268bd2; \ set -U fish_color_selection white --bold --background=brblack; \ set -U fish_color_search_match bryellow --background=black; \ set -U fish_color_history_current --bold; \ set -U fish_color_operator 00a6b2; \ set -U fish_color_param 839496; \ set -U fish_color_cwd green; \ set -U fish_color_cwd_root red; \ set -U fish_color_valid_path --underline; \ set -U fish_color_autosuggestion 586e75; \ set -U fish_color_user brgreen; \ set -U fish_color_escape 00a6b2; \ set -U fish_color_cancel -r; \ set -U fish_pager_color_completion B3A06D; \ set -U fish_pager_color_description B3A06D; \ set -U fish_pager_color_prefix cyan --underline; \ set -U fish_pager_color_progress brwhite --background=cyan; \ set -U fish_color_host normal; \ set -U fish_color_match --background=brblue; \ set -U fish_color_comment 586e75
Customize the greeting message with the variable
➜ set -U fish_greeting ""
Enable backwards-incompatible features.
➜ set -U fish_features stderr-nocaret qmark-noglob
Enable Vi mode.
For user’s who prefer Vi and Vim, there is a nifty method of enabling Vim keyboard shortcuts in addition to the default Emacs shortcuts. Creating the file below will enable this and default to insert mode.
function fish_user_key_bindings fish_default_key_bindings -M insert fish_vi_key_bindings insert end
Now, if you’re thinking about
.fishrc, forget about it.
The equivalent of
~/.config/fish/config.fish but a lot of configuration is better achieved - and organized - through universal variables and autoloading functions.
PATH environment variable in fish to suit your needs.
Version 3.2.0 of fish introduced the command
fish_add_path which makes permanently adding a path to
PATH super easy.
Just use use the command followed by the path to add, and that’s it!
No fiddling with configuration files necessary.
It takes care of duplicates for you, too.
Some Linux distributions don’t include
PATH by default.
/usr/local/bin to the end of the list in
PATH is as simple as this.
➜ fish_add_path /usr/local/bin
-p flag prepends the given path to
/usr/local/bin at the beginning of
PATH, use this command.
➜ fish_add_path -p /usr/local/bin
When messing with
PATH prior to version 3.2.0, use fish’s dedicated internal variable
This variable is special and populates
set command, prepend
PATH as follows.
➜ set -pU fish_user_paths /usr/local/bin
-p option prepends a value to the given variable.
-U option signifies a universal variable, which persists the variable in the future and across any currently running fish sessions.
This reduces the overhead of having to mess about with shell startup files.
These variables can be managed in the file
A lot of fish functionality has been covered already, but "there’s always more to learn" as they say. A few additional fish topics are covered here.
Export shell variables with the
This makes the variables accessible to other programs.
PATH is automatically exported from the contents of the un-exported
fish_user_paths variable, making this an exception.
For everything else, export the variable by calling
set with the
To add a value to the beginning of the
LD_LIBRARY_PATH environment variable and export it, use
set as follows.
➜ set -px LD_LIBRARY_PATH /usr/local/lib
Understand how fish handles PATH variables.
The fish shell stores lists internally as arrays of strings.
This is fundamentally different from how shells typically represent many fundamental variables which contain lists of paths, such as
Classic shells store these "lists" as a single string of colon-separated paths.
Many applications and programs expect the incumbent formatting, so fish treats these as special variables called PATH variables.
When printing or joining PATH variables, colons are used to delimit values when the variables are quoted.
Otherwise, spaces separate each path in the list.
Any variable ending in
PATH is automatically treated as a PATH variable.
So, when using
set to deal a PATH variable, you can still treat it as you would any other list in fish.
As an example, the following command adds
/usr/local/lib to the beginning of the classic
LD_LIBRARY_PATH variable and appends
~/lib to the end.
➜ set -x LD_LIBRARY_PATH /usr/local/lib $LD_LIBRARY_PATH ~/lib
When quoting the variable, it must be modified like so to achieve the same result.
➜ set -x LD_LIBRARY_PATH /usr/local/lib:"$LD_LIBRARY_PATH":~/lib
Take advantage of autosuggestions.
Just start typing a command and fish will provide suggestions on your prompt. As autosuggestion appear from your history, choose the suggested line with →. To select only the next word of the suggestion, use Alt+→.
Use fish’s searchable history instead of Bash’s reverse-search-history.
This one has required a bit of learning curve since I’m so used to finding previous commands by searching with Ctrl+R. With fish, this is even simpler. Start typing the letters, word, or phrase you want to match. Then, just press Alt+↑ to scroll backwards through history for matches.
Another nifty feature is the recursive wildcard which automatically descends into subdirectories for matching a particular pattern.
The following example recursively searches and lists all files ending in
➜ ls ~/.config/**.fish /home/jordan/.config/fish/functions/fish_prompt.fish
Common issues involve various environment variables and initialization functionality which assumes the default login shell is Bash.
Some people place
fish in their
~/.bashrc file to start fish from within Bash so that all environment variables are correctly configured.
I prefer to use
fish as my login shell and file bugs as necessary for projects to properly support it, but this is definitely a bit a of a pain but thus is progress, eh?
There’s a lot to learn about the fish shell. It provides an extremely convenient feature set and is attempting to solve issues inherited from shells of generations past. I love it, and its a great improvement to my workflow.