$linuxjunkies
>

Try the Fish Shell

Install Fish shell, configure autosuggestions, abbreviations, and functions, use the web UI, and understand where POSIX incompatibility will bite you.

BeginnerUbuntuDebianFedoraArch8 min readUpdated May 26, 2026

Before you start

  • A working terminal with sudo or root access
  • A supported Linux distro with a standard package manager
  • Basic familiarity with navigating the command line in any shell

Fish (the Friendly Interactive SHell) ships with autosuggestions, syntax highlighting, and a browser-based configuration UI out of the box — no plugin manager required. It is deliberately not POSIX-compliant, which means most bash scripts won't run in it, but interactive use is dramatically more pleasant. This guide walks you through installation, the features worth knowing immediately, and the gotchas to watch for.

Installing Fish

Debian and Ubuntu

sudo apt update && sudo apt install fish

Fedora

sudo dnf install fish

RHEL, Rocky Linux, AlmaLinux

Fish is not in the default RHEL repos. Enable the EPEL repository first, then install.

sudo dnf install epel-release
sudo dnf install fish

Arch Linux

sudo pacman -S fish

After installation, confirm the binary path — you'll need it in the next step.

which fish
# typical output: /usr/bin/fish

Setting Fish as Your Default Shell

First add fish to the list of valid login shells, then change your user's default. Skip the first command if fish is already listed in /etc/shells.

echo $(which fish) | sudo tee -a /etc/shells
chsh -s $(which fish)

Log out and back in for the change to take effect. To try fish without committing, just run fish in your current terminal session.

Autosuggestions and Syntax Highlighting

These are active immediately — no configuration needed. As you type, fish shows a greyed-out suggestion based on your history and the contents of $PATH. Press or End to accept the full suggestion, or Alt+→ to accept one word at a time. Commands that don't exist are shown in red; valid paths turn blue. This catches typos before you hit Enter.

Tab Completion

Fish reads man pages to generate completions automatically. Press Tab once to complete, twice to open an interactive menu you can navigate with arrow keys. Flag descriptions are shown inline, which makes exploring unfamiliar tools much faster.

git checkout --
# shows flags with short descriptions inline

Abbreviations

Abbreviations (abbr) are fish's preferred alternative to aliases. When you type an abbreviation and press Space or Enter, fish expands it in the command line before running it. That means your shell history records the full command, not a cryptic shorthand — which is much easier to audit and reuse.

Adding an abbreviation

abbr --add gst git status
abbr --add gco git checkout
abbr --add ll ls -lah

Abbreviations added this way persist across sessions automatically; fish saves them in ~/.config/fish/fish_variables. To remove one:

abbr --erase gst

To list all current abbreviations:

abbr --list

Functions

Fish functions replace shell scripts for per-user command customisation. Every function saved in ~/.config/fish/functions/ as functionname.fish is autoloaded on demand — fish does not source all of them at startup, so there is no startup cost.

Defining a function interactively

function mkcd
    mkdir -p $argv && cd $argv
end

Save it so it persists across sessions:

funcsave mkcd
# writes to ~/.config/fish/functions/mkcd.fish

Editing a saved function

funced mkcd

This opens the function body in your $EDITOR (or a built-in editor if none is set) and re-saves it when you close.

Viewing a function's source

functions mkcd

Web-Based Configuration (fish_config)

Fish ships with a browser UI for setting colours, prompts, functions, history, and variables — no text-file archaeology needed.

fish_config

This starts a local HTTP server (default port 8000) and opens your default browser. From here you can preview and apply built-in colour themes, change your prompt to one of the included options, and inspect your abbreviations and functions. Changes are written back to ~/.config/fish/ as plain fish script, so they are fully portable and version-controllable.

Environment Variables and PATH

Fish uses its own syntax for environment variables and does not source ~/.bashrc or ~/.profile. Set a persistent universal variable with set -Ux (universal, exported). Universal variables are shared across all fish sessions for your user immediately — no need to reload.

# Set a persistent exported variable
set -Ux EDITOR nvim

# Prepend to PATH persistently
fish_add_path ~/.local/bin

fish_add_path is the modern, idempotent way to extend PATH in fish; it avoids duplicates and persists across sessions. Avoid appending raw set -Ux fish_user_paths lines — that older pattern still works but fish_add_path is cleaner.

POSIX Incompatibility — What to Watch For

Fish is intentionally not POSIX-compliant. This is the most important thing to understand before switching. Practically, it means:

  • Bash scripts must still be run with bash. Running ./deploy.sh works fine as long as the shebang is #!/bin/bash, because the OS invokes bash directly. The problem occurs when people paste bash snippets directly into the fish prompt.
  • Variable syntax differs. Fish uses set VAR value not VAR=value. Bare export FOO=bar will throw an error in fish.
  • Command substitution uses parentheses. Use (command) instead of $(command) or backticks.
  • No && and || in older fish. Fish 3.0+ supports && and || — check your version with fish --version. On fish 3.x (all current distro packages), this is not an issue.
  • Condition syntax. Use if command; …; end — there is no [[ ]] or test shorthand at the prompt level, though test and [ still work as external commands.

For one-off bash commands in a fish session, prefix with bash -c or switch temporarily with bash.

Verifying Your Setup

# Check fish version
fish --version
# fish, version 3.7.1 (will vary)

# Confirm default shell changed
grep $USER /etc/passwd
# Should end in /usr/bin/fish

# Check a universal variable persists after a new session
set -Ux TEST_VAR hello
fish -c 'echo $TEST_VAR'
# hello

Troubleshooting

fish not in /etc/shells — chsh fails

If chsh refuses to set fish as your shell, the path is not listed in /etc/shells. Add it manually:

echo $(which fish) | sudo tee -a /etc/shells

SSH sessions still launch bash

Remote login shells read /etc/shells on the remote host. If fish is not installed on the server, it cannot be your login shell there. Set fish as the default only where it is installed, or use bash on the server and fish locally.

Startup is slow

Fish sources files in ~/.config/fish/conf.d/ at startup. If you have many third-party tool integrations (nvm, pyenv, etc.) that inject bash-style hooks, they add latency. Check what's in conf.d/ and evaluate whether each is necessary, or use Fisher (a plugin manager for fish) to load them lazily.

A script fails with "unexpected token"

You are likely running a bash script directly in fish. Check the shebang line. If there is none, either add #!/bin/bash to the script or run it explicitly with bash script.sh.

tested on:Ubuntu 24.04Fedora 40Arch rollingRocky 9

Frequently asked questions

Will my existing bash scripts break when I switch to fish?
No, as long as they have a proper shebang line like #!/bin/bash. The OS invokes the interpreter named in the shebang directly. Only scripts or snippets pasted bare into the fish prompt will fail due to syntax differences.
What is the difference between a fish abbreviation and an alias?
An alias substitutes the command at run time and your history records the alias name. An abbreviation expands visibly in the command line before execution, so history always shows the full real command — easier to audit and replay.
Does fish slow down shell startup compared to bash?
A plain fish startup is fast. Slowdowns come from conf.d/ files that run heavyweight init scripts (like nvm or pyenv hooks). Audit what's in ~/.config/fish/conf.d/ if startup feels sluggish.
Can I use fish on a remote server over SSH?
Yes, but fish must be installed on the server and listed in its /etc/shells. Many admins keep bash as the remote login shell and use fish only locally, which is a perfectly reasonable workflow.
How do I temporarily run a bash command from inside fish?
Prefix it with `bash -c 'your command here'`, or type `bash` to drop into an interactive bash session and `exit` to return to fish.

Related guides