โ† Back to all guides

Fish Shell TLDR

A rapid reference guide to the Fish shell. The friendly interactive shell with auto-suggestions and syntax highlighting out of the box.

v4.2.1 โ€” December 2025

What is Fish?

Fish (Friendly Interactive SHell) is a smart and user-friendly command line shell. Works great out of the box with features like autosuggestions, syntax highlighting, and web-based configuration.

Autosuggestions

Fish suggests commands as you type based on history and completions. Press โ†’ to accept.

Syntax Highlighting

Commands are colored as you type. Invalid commands are shown in red, valid ones in color.

Tab Completions

Rich tab completions with descriptions. Works for commands, paths, and command-specific options.

Sane Scripting

No word splitting, clean syntax with functions over aliases. Easier to write correct scripts.

Basics

Running Commands

# Simple command
echo Hello, World!

# Command with arguments
ls -la /home

# Chain commands with semicolon
cd /tmp; ls

# Command substitution
echo (date)
echo (ls | head -n 1)

# Logical operators
test -f file.txt; and echo "File exists"
test -f file.txt; or echo "File not found"

# Compound commands with braces (Fish 4.1+)
{ echo 1; echo 2 }

# Exit status (not $?)
echo $status

Comments

# This is a comment
echo foo # Inline comment

Help System

# Get help for any command
man ls
help set
help for

Variables

Fish uses the set command for all variable operations. No export command - use -x flag instead.

Setting Variables

# Local variable (function scope)
set name "Alice"

# Global variable (session-wide)
set -g name "Alice"

# Universal variable (all sessions, persists)
set -U name "Alice"

# Exported variable (available to child processes)
set -x PATH /usr/local/bin $PATH

# Combine flags
set -gx EDITOR vim

Using Variables

# Access with $
echo $name
echo "Hello, $name!"

# Default value if unset
echo $name; or echo "No name set"

# Check if variable exists
set -q name; and echo "Name is set"

Lists (Arrays)

# All variables are lists in Fish
set colors red green blue

# Access elements (1-indexed!)
echo $colors[1]      # red
echo $colors[2..3]  # green blue
echo $colors[-1]     # blue (last item)

# List length
echo (count $colors)

# Append to list
set -a colors yellow

# Prepend to list
set -p colors orange

# Remove from list
set -e colors[1]

Special Variables

# Exit status of last command
echo $status

# Exit status of all commands in pipeline
echo $pipestatus

# Function/script arguments
echo $argv
echo $argv[1]

# Process ID
echo $fish_pid

# Current working directory
echo $PWD

# User's home directory
echo $HOME

Variable Scopes

Scope Flag Lifetime
Local -l or default Current function/block
Global -g Current shell session
Universal -U All sessions, persists across reboots
Exported -x Available to child processes

Functions

Defining Functions

# Simple function
function greet
    echo "Hello!"
end

# Function with arguments
function greet
    echo "Hello, $argv[1]!"
end

# Function with description
function greet -d "Greet a user"
    echo "Hello, $argv[1]!"
end

# Function with argument names
function greet -a name
    echo "Hello, $name!"
end

Function Arguments

function example
    # $argv contains all arguments as a list
    echo "All args: "$argv

    # Access individual arguments
    echo "First: "$argv[1]
    echo "Second: "$argv[2]

    # Number of arguments
    echo "Count: "(count $argv)
end

# Call it
example foo bar baz

Saving Functions

# Save function to ~/.config/fish/functions/greet.fish
funcsave greet

# List all functions
functions

# Show function definition
functions greet

# Delete function
functions -e greet

Return Values

function is_root
    test (id -u) -eq 0
    # Last command's exit status becomes return value
end

function check
    return 1  # Explicit return with status code
end

# Use the return value
if is_root
    echo "Running as root"
end

Control Flow

If Statements

# Basic if
if test -f file.txt
    echo "File exists"
end

# If-else
if test -f file.txt
    echo "File exists"
else
    echo "File not found"
end

# If-else if-else
if test $count -gt 10
    echo "Many"
else if test $count -gt 5
    echo "Some"
else
    echo "Few"
end

# Using command success as condition
if grep -q "pattern" file.txt
    echo "Pattern found"
end

Test Operators

Test Description Example
-f File exists test -f file.txt
-d Directory exists test -d /tmp
-e Path exists test -e path
-z String is empty test -z $var
-n String is not empty test -n $var
= String equality test $a = $b
-eq Numeric equality test $a -eq $b
-gt Greater than test $a -gt $b
-lt Less than test $a -lt $b

Switch Statements

switch $animal
    case cat
        echo "Meow"
    case dog
        echo "Woof"
    case fish
        echo "Blub"
    case '*'
        echo "Unknown animal"
end

# Pattern matching with wildcards
switch $filename
    case '*.txt'
        echo "Text file"
    case '*.md'
        echo "Markdown file"
end

For Loops

# Iterate over list
for i in foo bar baz
    echo $i
end

# Iterate over range
for i in (seq 1 10)
    echo $i
end

# Iterate over files
for file in *.txt
    echo $file
end

# Iterate over command output
for line in (cat file.txt)
    echo $line
end

While Loops

# Basic while loop
set i 0
while test $i -lt 10
    echo $i
    set i (math $i + 1)
end

# While with command
while read -l line
    echo "Line: $line"
end < input.txt

Break and Continue

for i in (seq 1 10)
    if test $i -eq 5
        continue  # Skip to next iteration
    end

    if test $i -eq 8
        break  # Exit loop
    end

    echo $i
end

Strings & Lists

String Operations

# Concatenation (just place them next to each other)
set greeting "Hello, "$name"!"

# String length
string length "hello"  # 5

# Substring
string sub -s 1 -l 3 "hello"  # hel

# Replace
string replace "world" "Fish" "Hello world"  # Hello Fish

# Replace all
string replace -a "o" "0" "Hello world"  # Hell0 w0rld

# Match pattern
string match "*.txt" file.txt  # Returns file.txt if matches

# Case conversion
string upper "hello"  # HELLO
string lower "WORLD"  # world

# Trim whitespace
string trim "  hello  "  # hello

# Split string
string split ":" "a:b:c"  # Returns: a b c

# Join list into string
string join ", " a b c  # a, b, c

List Operations

# Create list
set fruits apple banana orange

# Count items
count $fruits

# Check if item in list
contains banana $fruits; and echo "Found!"

# Reverse list
for item in $fruits[-1..1]
    echo $item
end

Path Operations

# Get basename
path basename /home/user/file.txt  # file.txt

# Get dirname
path dirname /home/user/file.txt  # /home/user

# Get extension
path extension file.txt  # .txt

# Check if path exists
path exists /tmp; and echo "Exists"

I/O & Redirection

Output Redirection

# Redirect stdout to file (overwrite)
echo "hello" > file.txt

# Append to file
echo "world" >> file.txt

# Redirect stderr
command 2> error.log

# Redirect both stdout and stderr
command &> output.log

# Redirect stderr to stdout
command 2>&1

Pipes

# Basic pipe
ls | grep ".txt"

# Chain multiple pipes
cat file.txt | grep "pattern" | sort | uniq

# Pipe status (all exit codes in pipeline)
false | true
echo $pipestatus  # 1 0

Reading Input

# Read a line
read -l name
echo "Hello, $name!"

# Read with prompt
read -P "Enter your name: " -l name

# Read from file
while read -l line
    echo $line
end < file.txt

Command Substitution

# Capture command output
set current_date (date)
set files (ls)

# Use in strings
echo "Today is (date)"

# Nested substitution
set count (count (ls))

Abbreviations & Aliases

Abbreviations

Abbreviations expand when you press space or enter. Unlike aliases, you see the full command before executing.

# Create abbreviation
abbr -a gs "git status"
abbr -a gc "git commit"
abbr -a gp "git push"

# List all abbreviations
abbr -l

# Remove abbreviation
abbr -e gs

# Position-specific (only expands if it's first word)
abbr -a --position command ls "ls -la"

Aliases (via Functions)

Fish doesn't have aliases. Use functions instead, which are more powerful.

# Simple alias-style function
function ll
    ls -la $argv
end
funcsave ll

# Alias with default arguments
function grep
    command grep --color=auto $argv
end
funcsave grep
Abbreviations are saved automatically to ~/.config/fish/fish_variables. Functions need funcsave or manual saving.

Completions

Custom Completions

# Complete command with options
complete -c mycommand -s h -l help -d "Show help"
complete -c mycommand -s v -l version -d "Show version"

# Complete with file paths
complete -c mycommand -s f -l file -r -F

# Complete with directory paths
complete -c mycommand -s d -l dir -r -a "(__fish_complete_directories)"

# Complete with custom options
complete -c mycommand -s m -l mode -r -a "dev prod test"

Completion Flags

Flag Description
-c CMD Command to complete
-s Short option (single letter)
-l Long option
-d Description
-r Requires argument
-a Possible argument values
-F Complete with filenames

Configuration

Config Files

# Main config: ~/.config/fish/config.fish
# Add to config.fish:

# Set environment variables
set -gx EDITOR vim
set -gx PATH /usr/local/bin $PATH

# Aliases (via functions)
function ll
    ls -la $argv
end

# Abbreviations
abbr -a gs "git status"
abbr -a gc "git commit"

File Locations

File Purpose
~/.config/fish/config.fish Main configuration file
~/.config/fish/functions/ Custom function definitions
~/.config/fish/completions/ Custom completions
~/.config/fish/conf.d/ Additional config snippets (auto-loaded)
~/.local/share/fish/fish_history Command history
~/.config/fish/fish_variables Universal variables

Prompt Customization

# Simple custom prompt
function fish_prompt
    echo (whoami)@(hostname) (pwd) '> '
end

# With colors
function fish_prompt
    set_color blue
    echo -n (whoami)
    set_color normal
    echo -n ' at '
    set_color yellow
    echo -n (prompt_pwd)
    set_color normal
    echo -n ' > '
end

# Use a prompt theme (via fisher or similar)
# Popular: tide, pure, bobthefish

Key Bindings

# Vi mode
fish_vi_key_bindings

# Emacs mode (default)
fish_default_key_bindings

# Custom key binding
function fish_user_key_bindings
    bind \cf forward-char  # Ctrl+F
    bind \cl clear-screen  # Ctrl+L
end

# Find key name
fish_key_reader

Web Configuration

# Open web-based configuration UI
fish_config

# Configure specific aspects
fish_config prompt
fish_config colors

Package Management

# Fisher - popular Fish plugin manager
# Install Fisher first:
curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source && fisher install jorgebucaran/fisher

# Install plugins
fisher install jorgebucaran/nvm.fish
fisher install PatrickF1/fzf.fish

# List installed plugins
fisher list

# Update all plugins
fisher update
Fish automatically loads all .fish files from ~/.config/fish/conf.d/ on startup. Use this for modular configuration.