Zsh was my default shell for many years. I began with Linux and used Bash as my first shell. After trying various Linux distributions (Ubuntu, Cinnamon, Manjaro, Arch), I found myself frequently modifying and fixing my .bashrc
. When I switched to a MacBook Pro, I adopted Zsh, enhanced by Oh-My-Zsh, as my default shell for a long time.
A few weeks ago, I discovered a new shell named fish
. It’s fast, smart, and offers impressive built-in plugins and themes. I decided to make Fish my default shell and explore its features.
Fish Shell 🐠
Fish (short for friendly interactive shell, stylized in lowercase) is a Unix-like shell focused on interactivity and usability. Unlike other shells, Fish is feature-rich by default rather than highly configurable. It’s considered an exotic shell because it doesn’t adhere to POSIX shell standards by design.
Why Switch?
- Startup Delays: Over time, I noticed minor delays when starting Zsh on macOS. While small, these delays became more noticeable. Fast systems give better feedback, as explained by the Doherty Threshold principle.
- Pre-Built Features: My
.zshrc
included various plugins and configurations provided byoh-my-zsh
. Fish offers most of these features out of the box.
With this in mind, I aimed to replicate my Zsh setup in Fish efficiently.
Getting Started
Installing Fish
On macOS, install Fish using Homebrew:
$ brew install fish
$ fish --version
fish, version 3.6.1
Setting Fish as the Default Shell
Add Fish to the list of available shells and set it as the default shell:
$ echo /opt/homebrew/bin/fish | sudo tee -a /etc/shells
$ chsh -s /opt/homebrew/bin/fish
Recognizing Homebrew Binaries
Ensure Fish recognizes Homebrew binaries by adding them to Fish’s path:
$ fish_add_path "/opt/homebrew/bin/"
$ which brew
/opt/homebrew/bin/brew
Updating Completions
Generate completion files to enable better command suggestions:
$ fish_update_completions
Installing Oh-My-Fish
Like Oh-My-Zsh, Fish can be enhanced using Oh-My-Fish. Install it with:
$ curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | fish
Using Fisher for Plugin Management
Fisher is a powerful plugin manager for Fish:
$ brew install fisher
Basics
The Fish configuration file, equivalent to .bashrc
or .zshrc
, is located at ~/.config/fish/config.fish
. Fish uses a unique scripting language with syntax that differs slightly from Bash. For instance:
- Blocks like
if
,while
, andfor
are enclosed withend
. - Functions are defined with the
function
keyword and saved in.fish
files.
Abbreviations, Aliases, and Functions
- Abbreviations: Short text expands into longer commands after pressing space or enter.
- Aliases: Defined in
~/.config/fish/config.fish
. In Fish,alias
is a wrapper forfunction
. - Functions: Saved under
~/.config/fish/functions
.
Example:
$ alias lsa="ls -la"
This creates a Fish function with the description alias lsa=ls -la
. You can view it by pressing the tab
key for completions.
To migrate aliases from .zshrc
, paste them into the terminal and use funcsave
to save them as functions:
$ funcsave lsa
Environment Variables
Use Universal Variables to set environment variables:
$ set -Ux EDITOR "vim"
This writes the variable to ~/.config/fish/fish_variables
and exports it to all active shells.
Plugins
Use Fisher and Oh-My-Fish to install helpful plugins:
$ fisher install patrickf1/fzf.fish
$ fisher install edc/bash
$ fisher install laughedelic/pisces
Additionally, install plugins like:
$ omf install z extract kill-on-port nvm android-sdk
Prompts
I use Starship, a cross-shell prompt manager. To enable it in Fish:
$ brew install starship
$ echo "starship init fish | source" >> $HOME/.config/fish/config.fish
Another great option is Tide. Install it with Fisher:
$ fisher install IlanCosman/tide@v6
$ tide configure
Benefits
Switching to Fish made my shell configuration more organized and faster. Fish scripts are more readable compared to Zsh or Bash. Here are some features I love:
- Command autosuggestions with
fish_update_completions
. - Fuzzy search for history (
CTRL + R
) usingfzf
. - Fast, pre-built plugins.
Challenges
While the migration was quick, I faced a few challenges:
- Some Zsh functions required rewriting due to syntax differences.
- Converting Zsh history using zsh-history-to-fish worked, but timestamps and multiline commands needed manual fixes.
- Scripts exporting ENV variables (
export VAR=value
) caused errors in Fish.
Conclusion
Migrating from Zsh to Fish took less than 10 minutes, excluding minor adjustments. Fish feels faster, cleaner, and more intuitive. If you’re curious, give it a try! You can always revert to your previous setup if it doesn’t work for you.