# A practical Fish shell guide

[Fish](https://fishshell.com/) 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](https://stream.mux.com/nALvs002E00YxiYG3c02Vsyu8Se00Ot3P00WrqYcZGL502Cuc.m3u8)

## Install Fish

Install Fish with [Homebrew](https://brew.sh/):

```shell
brew install fish
fish
```

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

Check the installed version with:

```shell
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:

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

Do this once, then change your login shell:

```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`:

```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:

```shell
fish_config
```

You can also browse themes without leaving the terminal:

```shell
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:

```fish
abbr --add gco git checkout
```

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

```fish
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](https://github.com/jorgebucaran/fisher) is a small plugin manager for
Fish. Run its current installation command **from Fish**:

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

The essential commands are:

```fish
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](https://github.com/jorgebucaran/nvm.fish):

```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:

```fish
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:

```shell
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:

- [Fish tutorial](https://fishshell.com/docs/current/tutorial.html)
- [Fish for Bash users](https://fishshell.com/docs/current/fish_for_bash_users.html)
- [Awesome Fish](https://github.com/jorgebucaran/awsm.fish)
