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
~/.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 files from ~/.config/fish/conf.d/ on startup. Use this for modular configuration.