Try the Fish Shell
Install Fish shell, configure autosuggestions, abbreviations, and functions, use the web UI, and understand where POSIX incompatibility will bite you.
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.shworks 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 valuenotVAR=value. Bareexport FOO=barwill 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 withfish --version. On fish 3.x (all current distro packages), this is not an issue. - Condition syntax. Use
if command; …; end— there is no[[ ]]ortestshorthand at the prompt level, thoughtestand[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.
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
Bash Arrays and Associative Arrays
Master bash indexed and associative arrays: declaration, element access, looping, mapfile, namerefs, and practical patterns for real scripting work.
Bash Functions and Variable Scoping
Master Bash function scoping with local variables, source-based libraries, correct use of return codes, and array passing techniques including namerefs.
Bash Loops: for, while and until
Learn all three Bash loop types — for, while, and until — with practical, copy-paste examples covering file iteration, counting, polling, and safe line reading.
Bash Scripting for Beginners
Learn Bash scripting from scratch: shebang lines, variables, conditionals, loops, and arguments, plus a real backup script to tie it all together.