A practical Fish shell guide

Fish is a friendly interactive shell with excellent autosuggestions, syntax highlighting, and tab completion out of the box. It is fast to set up and pleasant to use without a large configuration framework.

This guide covers a small, modern Fish setup on macOS.

Fish shell auto-completes almost everything

Install Fish

Install Fish with Homebrew:

brew install fish
fish

You are now running Fish. Use exit to return to your previous shell.

Check the installed version with:

fish --version

Homebrew uses different prefixes on Apple Silicon and Intel Macs, so avoid hard-coding /opt/homebrew/bin/fish or /usr/local/bin/fish. command -v fish will find the correct path.

Make Fish your default shell

First, add Fish to the list of allowed login shells:

command -v fish | sudo tee -a /etc/shells

Do this once, then change your login shell:

chsh -s "$(command -v fish)"

Restart your terminal. If iTerm2 still opens another shell, set Profiles → General → Command to Login Shell.

On Linux, setting Fish as the login shell can bypass setup normally performed by /etc/profile. Follow your distribution's instructions or configure your terminal to start fish directly.

Configure Fish

Fish stores user configuration in ~/.config/fish. The main startup file is ~/.config/fish/config.fish:

set --global --export EDITOR nvim
fish_add_path ~/.local/bin
 
if status is-interactive
    # Interactive-only setup
end

config.fish is not a direct replacement for .bash_profile: it runs for every Fish shell. Use status is-interactive or status is-login when a command should only run in one kind of session.

To customize colors and the prompt in your browser, run:

fish_config

You can also browse themes without leaving the terminal:

fish_config theme show

Abbreviations and functions

For typing shortcuts, prefer an abbreviation. Fish expands it before the command runs, so you can see and edit the real command:

abbr --add gco git checkout

Use a function when you need behavior rather than text replacement:

function week --description 'Show the current ISO week number'
    date +%V
end
 
funcsave week

funcsave writes the function to ~/.config/fish/functions/week.fish, where Fish can autoload it in future sessions.

Install plugins with Fisher

Fisher is a small plugin manager for Fish. Run its current installation command from Fish:

curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher

The essential commands are:

fisher install owner/plugin
fisher list
fisher update
fisher remove owner/plugin

Fisher records installed plugins in ~/.config/fish/fish_plugins, more precisely, $__fish_config_dir/fish_plugins. Put that file in your dotfiles, then run fisher update on a new machine to install the same set.

Manage Node.js versions

The Bash-based nvm script is not native to Fish. The simplest alternative is nvm.fish:

fisher install jorgebucaran/nvm.fish
nvm install lts

It supports .nvmrc and .node-version files without a Bash compatibility layer.

Run Bash scripts when needed

Fish is deliberately not POSIX-compatible, but executable Bash scripts still work when they have a correct shebang such as #!/usr/bin/env bash.

For a one-off Bash command, run:

bash -c 'some_bash_command'

Scripts that must be sourced to modify the current shell cannot change Fish's environment directly. Prefer a Fish-native integration for those tools.

Update Fish

If you installed Fish with Homebrew:

brew update
brew upgrade fish

That is the whole setup: keep config.fish, your saved functions, and fish_plugins under version control, and let Fish handle the rest.

More useful references: