I just got a new MacBook and couldn't remember how to set up the fish shell the way I like ๐คท.
Below you can find my notes on setting it up from scratch on macOS.
Setup
Install fish
Run the following command to install fish using Homebrew:
brew install fish
Set fish as the default shell
Run the following command to 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
echo /opt/homebrew/bin/fish | sudo tee -a /etc/shells
chsh -s /opt/homebrew/bin/fish
Make fish recognize Homebrew's binaries
Add the Homebrew's binaries to the list of fish known binaries (otherwise it won't run commands installed with brew
) with the following command:
fish_add_path "/opt/homebrew/bin/"
fish_add_path "/opt/homebrew/bin/"
Create completition files
Parse manual pages installed on the system and attempt to create completion files in the fish
configuration directory with the following command:
fish_update_completions
Customizations
Install fisher
and fisher
plugins:
- Install the
fisher
plugin manager
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher
- Install
z
to quickly jump across known directories:
fisher install jethrokuan/z
fisher install jethrokuan/z
- Install
fzf
, a CLI fuzzy-finder, and make it work amazingly well with fish:
brew install fzf && fisher install patrickf1/fzf.fish
brew install fzf && fisher install patrickf1/fzf.fish
- Install a plugin that avoids issues where fish doesn't recognize global
npm
scripts:
fisher install rstacruz/fish-npm-global
fisher install rstacruz/fish-npm-global
- Install a fish-compatible version of
nvm
:
fisher install jorgebucaran/nvm.fish
fisher install jorgebucaran/nvm.fish
Change the fish shell prompt (color/theme)
Add the following to ~/.config/fish/functions/fish_prompt.fish
to change the fish theme:
function fish_prompt
set -l pointer_color red
test $status = 0; and set pointer_color yellow
set -q __fish_git_prompt_showupstream
or set -g __fish_git_prompt_showupstream auto
if not set -q VIRTUAL_ENV_DISABLE_PROMPT
set -g VIRTUAL_ENV_DISABLE_PROMPT true
end
set_color yellow
printf '%s' $USER
set_color normal
printf ' in '
set_color $fish_color_cwd
printf '%s' (prompt_pwd --dir-length=0)
set_color normal
# git
set_color white -d
set -l prompt_git (fish_git_prompt '%s')
test -n "$prompt_git"
and printf ' %s' $prompt_git
set_color normal
# Line 2
echo
set_color $pointer_color
printf 'ยป '
set_color normal
end
function fish_prompt
set -l pointer_color red
test $status = 0; and set pointer_color yellow
set -q __fish_git_prompt_showupstream
or set -g __fish_git_prompt_showupstream auto
if not set -q VIRTUAL_ENV_DISABLE_PROMPT
set -g VIRTUAL_ENV_DISABLE_PROMPT true
end
set_color yellow
printf '%s' $USER
set_color normal
printf ' in '
set_color $fish_color_cwd
printf '%s' (prompt_pwd --dir-length=0)
set_color normal
# git
set_color white -d
set -l prompt_git (fish_git_prompt '%s')
test -n "$prompt_git"
and printf ' %s' $prompt_git
set_color normal
# Line 2
echo
set_color $pointer_color
printf 'ยป '
set_color normal
end
Allow restarting the shell by running fish
Back in the day, with zsh I was used to restart the shell by running zsh
. To do the same with fish, create a new funcion ~/.config/fish/functions/fish.fish
:
# A way to reload the shell ร la "zsh"
function fish
source ~/.config/fish/config.fish
end
# A way to reload the shell ร la "zsh"
function fish
source ~/.config/fish/config.fish
end
Print a new line in the CLI after any command
To print a new line after any command create a new function ~/.config/fish/functions/postexec_newline.fish
:
# Print a new line after any command
# https://stackoverflow.com/a/70644608
function postexec_newline --on-event fish_postexec
echo
end
# Print a new line after any command
# https://stackoverflow.com/a/70644608
function postexec_newline --on-event fish_postexec
echo
end
Tweak the config file
Here's my ~/.config/fish/config.fish
file.
Protip: when running/customizing external programs in the config file it is a good practice to check if the program executable is available (with if type -q COMMAND_NAME
) to avoid errors.
if status is-interactive
# Commands to run in interactive sessions can go here
end
# Disable the fish greeting message
set fish_greeting ""
# Print a new line after any command
source ~/.config/fish/functions/postexec_newline.fish
# Setup brew
eval "$(/opt/homebrew/bin/brew shellenv)"
# Clear line on CTRL + C
# Sometimes it still doesn't work well enough on node.js scripts :(
bind --preset \cC 'cancel-commandline'
# Auto-switch nvm version on cd
# Requires a ~/.node-version file with a valid node version
# https://github.com/jorgebucaran/nvm.fish/pull/186
if type -q nvm
function __nvm_auto --on-variable PWD
nvm use --silent 2>/dev/null # Comment out the silent flag for debugging
end
__nvm_auto
end
# Pyenv setup
# Requires `brew install pyenv`
if type -q pyenv
status --is-interactive; and source (pyenv init -|psub)
end
# `ls` โ `ls -laG` abbreviation
abbr -a -g ls ls -laG
# `ls` โ `exa` abbreviation
# Requires `brew install exa`
if type -q exa
abbr --add -g ls 'exa --long --classify --all --header --git --no-user --tree --level 1'
end
# `cat` โ `bat` abbreviation
# Requires `brew install bat`
if type -q bat
abbr --add -g cat 'bat'
end
# `rm` โ `trash` abbreviation (moves files to the trash instead of deleting them)
# Requires `brew install trash`
if type -q trash
abbr --add -g rm 'trash'
end
if status is-interactive
# Commands to run in interactive sessions can go here
end
# Disable the fish greeting message
set fish_greeting ""
# Print a new line after any command
source ~/.config/fish/functions/postexec_newline.fish
# Setup brew
eval "$(/opt/homebrew/bin/brew shellenv)"
# Clear line on CTRL + C
# Sometimes it still doesn't work well enough on node.js scripts :(
bind --preset \cC 'cancel-commandline'
# Auto-switch nvm version on cd
# Requires a ~/.node-version file with a valid node version
# https://github.com/jorgebucaran/nvm.fish/pull/186
if type -q nvm
function __nvm_auto --on-variable PWD
nvm use --silent 2>/dev/null # Comment out the silent flag for debugging
end
__nvm_auto
end
# Pyenv setup
# Requires `brew install pyenv`
if type -q pyenv
status --is-interactive; and source (pyenv init -|psub)
end
# `ls` โ `ls -laG` abbreviation
abbr -a -g ls ls -laG
# `ls` โ `exa` abbreviation
# Requires `brew install exa`
if type -q exa
abbr --add -g ls 'exa --long --classify --all --header --git --no-user --tree --level 1'
end
# `cat` โ `bat` abbreviation
# Requires `brew install bat`
if type -q bat
abbr --add -g cat 'bat'
end
# `rm` โ `trash` abbreviation (moves files to the trash instead of deleting them)
# Requires `brew install trash`
if type -q trash
abbr --add -g rm 'trash'
end