Appearance
Fish - Friendly Interactive Shell
Fish (Friendly Interactive Shell) is a smart and user-friendly command line shell designed for interactive use, usability, and discoverability. Created by Axel Liljencrantz in 2005, Fish takes a different approach from traditional shells by prioritizing user experience and providing sensible defaults out of the box. Unlike POSIX-compliant shells, Fish features syntax highlighting, autosuggestions, tab completions that work without configuration, and a clean, modern syntax that aims to be more intuitive and less error-prone than traditional shell languages.
Installation and Setup
Installing Fish Shell
fish
# Ubuntu/Debian
sudo apt update && sudo apt install fish
# CentOS/RHEL/Fedora
sudo dnf install fish
# macOS (using Homebrew)
brew install fish
# Arch Linux
sudo pacman -S fish
# FreeBSD
pkg install fish
# From source (latest version)
git clone https://github.com/fish-shell/fish-shell.git
cd fish-shell
cmake .
make
sudo make install
Setting Fish as Default Shell
fish
# Check Fish installation
which fish
/usr/bin/fish
# Add Fish to available shells
echo /usr/bin/fish | sudo tee -a /etc/shells
# Set Fish as default shell
chsh -s /usr/bin/fish
# Verify change (restart terminal)
echo $SHELL
First-Time Setup
fish
# Start Fish shell
fish
# Fish will create configuration directory automatically
# ~/.config/fish/
# Run Fish configuration wizard
fish_config
# This opens a web-based configuration interface
# Accessible at http://localhost:8000
Basic Configuration Structure
fish
# Fish configuration files
~/.config/fish/config.fish # Main configuration file
~/.config/fish/functions/ # Custom functions directory
~/.config/fish/completions/ # Custom completions directory
~/.config/fish/conf.d/ # Additional configuration files
# Create basic config.fish
mkdir -p ~/.config/fish
cat > ~/.config/fish/config.fish << 'EOF'
# Fish configuration
# Set environment variables
set -gx EDITOR vim
set -gx BROWSER firefox
# Add to PATH
set -gx PATH $HOME/bin $PATH
set -gx PATH $HOME/.local/bin $PATH
# Aliases
alias ll 'ls -alF'
alias la 'ls -A'
alias l 'ls -CF'
# Custom greeting
set fish_greeting "Welcome to Fish Shell!"
EOF
Fish Syntax and Language Features
Variables and Scope
fish
# Variable assignment (no $ for assignment)
set name "John Doe"
set age 30
set path "/home/user"
# Using variables ($ required for expansion)
echo $name
echo "Hello, $name"
echo "Age: $age"
# Variable scopes
set -l local_var "local" # Local to current scope
set -g global_var "global" # Global to current session
set -U universal_var "universal" # Universal across all sessions
set -x exported_var "exported" # Exported to child processes
# Multiple assignment
set fruits apple banana orange
echo $fruits[1] # apple (1-indexed)
echo $fruits[2] # banana
echo $fruits[-1] # orange (last element)
# Array operations
set fruits $fruits grape # Append
set fruits[2] kiwi # Replace element
set -e fruits[1] # Erase element
Command Substitution
fish
# Command substitution using parentheses
set current_date (date)
set file_count (ls | wc -l)
set user_home (eval echo ~$USER)
# Nested command substitution
echo "Today is "(date +%A)", "(date +%B)" "(date +%d)
# Store command output in variable
set git_branch (git branch --show-current 2>/dev/null)
if test -n "$git_branch"
echo "Current branch: $git_branch"
end
String Manipulation
fish
# String operations
set string "Hello, World!"
echo (string length "$string") # String length
echo (string sub -s 1 -l 5 "$string") # Substring (Hello)
echo (string replace "Hello" "Hi" "$string") # Replace
echo (string upper "$string") # Uppercase
echo (string lower "$string") # Lowercase
# String splitting and joining
set words (string split " " "$string")
echo $words[1] # Hello,
set rejoined (string join "-" $words)
echo $rejoined # Hello,-World!
# Pattern matching
if string match -q "Hello*" "$string"
echo "String starts with Hello"
end
# Regular expressions
if string match -qr "W\w+d" "$string"
echo "String contains word starting with W and ending with d"
end
Conditional Statements
fish
# if-then-else
if test $age -gt 18
echo "Adult"
else if test $age -eq 18
echo "Just turned adult"
else
echo "Minor"
end
# Test conditions
if test -f "file.txt" # File exists
echo "File exists"
end
if test -d "directory" # Directory exists
echo "Directory exists"
end
if test "$var" = "value" # String equality
echo "Variable equals value"
end
if test $num -eq 10 # Numeric equality
echo "Number is 10"
end
# Logical operators
if test $age -gt 18; and test $age -lt 65
echo "Working age"
end
if test $status -eq 0; or test $force = "true"
echo "Success or forced"
end
if not test -f "file.txt"
echo "File does not exist"
end
# Switch statement
switch $file_extension
case "txt"
echo "Text file"
case "jpg" "png" "gif"
echo "Image file"
case "*"
echo "Unknown file type"
end
Loops and Iteration
fish
# for loop
for i in (seq 1 10)
echo "Number: $i"
end
for file in *.txt
echo "Processing: $file"
end
for item in $array
echo "Item: $item"
end
# while loop
set counter 1
while test $counter -le 10
echo "Counter: $counter"
set counter (math $counter + 1)
end
# Loop control
for i in (seq 1 10)
if test $i -eq 5
continue # Skip iteration
end
if test $i -eq 8
break # Exit loop
end
echo $i
end
Functions and Scripting
Function Definition
fish
# Basic function
function greet
echo "Hello, $argv[1]!"
end
# Function with description
function greet --description "Greet a user"
echo "Hello, $argv[1]!"
end
# Function with argument validation
function calculate_sum --description "Calculate sum of two numbers"
if test (count $argv) -ne 2
echo "Usage: calculate_sum <num1> <num2>"
return 1
end
math $argv[1] + $argv[2]
end
# Function with local variables
function process_file --description "Process a file"
set -l filename $argv[1]
set -l line_count (wc -l < "$filename")
echo "File $filename has $line_count lines"
end
Advanced Function Features
fish
# Function with options
function my_ls --description "Enhanced ls with options"
argparse 'l/long' 'a/all' 'h/help' -- $argv
or return
if set -q _flag_help
echo "Usage: my_ls [-l|--long] [-a|--all] [directory]"
return
end
set -l ls_args
if set -q _flag_long
set ls_args $ls_args -l
end
if set -q _flag_all
set ls_args $ls_args -a
end
ls $ls_args $argv
end
# Function with completion
function mycommand --description "Custom command with completion"
switch $argv[1]
case "start"
echo "Starting service"
case "stop"
echo "Stopping service"
case "status"
echo "Service status"
case "*"
echo "Usage: mycommand {start|stop|status}"
end
end
# Save function permanently
funcsave greet
funcsave calculate_sum
Error Handling
fish
# Function with error handling
function safe_copy --description "Safe file copy with error handling"
if test (count $argv) -ne 2
echo "Error: Exactly two arguments required" >&2
return 1
end
set -l source $argv[1]
set -l dest $argv[2]
if not test -f "$source"
echo "Error: Source file '$source' does not exist" >&2
return 1
end
if test -f "$dest"
echo "Warning: Destination file '$dest' already exists"
read -P "Overwrite? (y/N): " -l confirm
if test "$confirm" != "y"
echo "Copy cancelled"
return 1
end
end
if cp "$source" "$dest"
echo "Successfully copied '$source' to '$dest'"
else
echo "Error: Failed to copy file" >&2
return 1
end
end
Interactive Features
Autosuggestions
fish
# Autosuggestions are enabled by default
# Type a command and Fish will suggest completions based on:
# - Command history
# - Valid file paths
# - Command completions
# Accept suggestion: Right arrow or Ctrl+F
# Accept single word: Alt+Right arrow or Alt+F
# Dismiss suggestion: Escape
# Configure autosuggestion color
set -g fish_color_autosuggestion 555
Tab Completions
fish
# Tab completions work automatically for:
# - Commands in PATH
# - File and directory names
# - Command-specific options and arguments
# Custom completion for your function
complete -c mycommand -a "start stop status restart" -d "Service commands"
complete -c mycommand -s h -l help -d "Show help"
complete -c mycommand -s v -l verbose -d "Verbose output"
# File-based completion
complete -c myapp -a "(__fish_complete_suffix .conf)" -d "Configuration files"
# Conditional completion
complete -c git -n "__fish_git_needs_command" -a "add commit push pull"
complete -c git -n "__fish_git_using_command add" -a "(__fish_git_modified_files)"
Syntax Highlighting
fish
# Syntax highlighting is automatic and includes:
# - Valid/invalid commands (green/red)
# - Strings and quotes
# - Variables and expansions
# - Comments
# Customize syntax highlighting colors
set -g fish_color_command blue
set -g fish_color_param cyan
set -g fish_color_redirection yellow
set -g fish_color_comment brblack
set -g fish_color_error red
set -g fish_color_escape bryellow
set -g fish_color_operator green
set -g fish_color_quote yellow
set -g fish_color_valid_path --underline
History and Search
fish
# History search (automatic)
# Type partial command and use Up/Down arrows
# History search with specific text
# Type text and press Ctrl+R for reverse search
# History commands
history # Show all history
history search "git" # Search history for "git"
history delete --prefix "rm" # Delete commands starting with "rm"
history clear # Clear all history
# Configure history
set -g fish_history_max 10000 # Maximum history entries
Configuration and Customization
Environment Variables
fish
# Set environment variables
set -gx EDITOR vim
set -gx BROWSER firefox
set -gx PAGER less
# PATH manipulation
set -gx PATH $HOME/bin $PATH
set -gx PATH $HOME/.local/bin $PATH
set -gx PATH /usr/local/bin $PATH
# Remove from PATH
set -l index (contains -i /unwanted/path $PATH)
if test $index -gt 0
set -e PATH[$index]
end
# Conditional environment variables
if test -d "$HOME/.cargo/bin"
set -gx PATH $HOME/.cargo/bin $PATH
end
# Platform-specific variables
switch (uname)
case Darwin
set -gx HOMEBREW_PREFIX /opt/homebrew
case Linux
set -gx XDG_CONFIG_HOME $HOME/.config
end
Aliases and Abbreviations
fish
# Aliases (expanded when defined)
alias ll 'ls -alF'
alias la 'ls -A'
alias l 'ls -CF'
alias grep 'grep --color=auto'
# Abbreviations (expanded when typed)
abbr -a g git
abbr -a gc 'git commit'
abbr -a gp 'git push'
abbr -a gl 'git log --oneline'
abbr -a gst 'git status'
# Conditional abbreviations
if command -v docker >/dev/null
abbr -a d docker
abbr -a dc 'docker-compose'
abbr -a dps 'docker ps'
end
# List and manage abbreviations
abbr -l # List all abbreviations
abbr -e gc # Erase abbreviation
Prompt Customization
fish
# Simple prompt function
function fish_prompt
set_color green
echo -n (whoami)
set_color normal
echo -n "@"
set_color blue
echo -n (hostname)
set_color normal
echo -n ":"
set_color yellow
echo -n (prompt_pwd)
set_color normal
echo -n "\$ "
end
# Advanced prompt with Git integration
function fish_prompt
set -l last_status $status
# User and host
set_color green
echo -n (whoami)
set_color normal
echo -n "@"
set_color blue
echo -n (hostname)
set_color normal
# Current directory
echo -n ":"
set_color yellow
echo -n (prompt_pwd)
set_color normal
# Git information
if git rev-parse --git-dir >/dev/null 2>&1
set -l branch (git branch --show-current 2>/dev/null)
if test -n "$branch"
echo -n " ("
set_color cyan
echo -n "$branch"
set_color normal
# Check for changes
if not git diff --quiet 2>/dev/null
set_color red
echo -n "*"
set_color normal
end
echo -n ")"
end
end
# Prompt symbol based on last command status
if test $last_status -eq 0
set_color green
echo -n " ❯ "
else
set_color red
echo -n " ❯ "
end
set_color normal
end
# Right prompt
function fish_right_prompt
set_color brblack
echo -n (date "+%H:%M:%S")
set_color normal
end
Theme and Color Configuration
fish
# Use fish_config for GUI configuration
fish_config
# Or set colors manually
set -g fish_color_normal normal
set -g fish_color_command blue
set -g fish_color_quote yellow
set -g fish_color_redirection cyan
set -g fish_color_end green
set -g fish_color_error red
set -g fish_color_param cyan
set -g fish_color_comment brblack
set -g fish_color_match --background=brblue
set -g fish_color_selection white --bold --background=brblack
set -g fish_color_search_match bryellow --background=brblack
set -g fish_color_history_current --bold
set -g fish_color_operator green
set -g fish_color_escape bryellow
set -g fish_color_cwd green
set -g fish_color_cwd_root red
set -g fish_color_valid_path --underline
set -g fish_color_autosuggestion 555
set -g fish_color_user brgreen
set -g fish_color_host normal
set -g fish_color_cancel -r
set -g fish_pager_color_completion normal
set -g fish_pager_color_description B3A06D yellow
set -g fish_pager_color_prefix white --bold --underline
set -g fish_pager_color_progress brwhite --background=cyan
Package Management and Plugins
Fisher Plugin Manager
fish
# Install Fisher
curl -sL https://git.io/fisher | source && fisher install jorgebucaran/fisher
# Install plugins
fisher install jorgebucaran/nvm.fish
fisher install PatrickF1/fzf.fish
fisher install franciscolourenco/done
fisher install jethrokuan/z
# List installed plugins
fisher list
# Update plugins
fisher update
# Remove plugin
fisher remove jorgebucaran/nvm.fish
Popular Fish Plugins
fish
# z - Directory jumping
fisher install jethrokuan/z
# Usage: z partial_directory_name
# fzf integration
fisher install PatrickF1/fzf.fish
# Provides Ctrl+R for history search, Ctrl+Alt+F for file search
# done - Desktop notifications
fisher install franciscolourenco/done
# Notifies when long-running commands complete
# nvm for Node.js
fisher install jorgebucaran/nvm.fish
nvm install node
nvm use node
# autopair - Automatic bracket pairing
fisher install jorgebucaran/autopair.fish
# bass - Run Bash utilities in Fish
fisher install edc/bass
bass source ~/.bashrc
Manual Plugin Installation
fish
# Create functions directory
mkdir -p ~/.config/fish/functions
# Download and install plugin manually
curl -Lo ~/.config/fish/functions/fisher.fish --create-dirs https://git.io/fisher
# Install from local directory
git clone https://github.com/user/plugin.git
cp plugin/*.fish ~/.config/fish/functions/
Advanced Features and Tips
Math Operations
fish
# Math command for calculations
math 5 + 3 # 8
math 10 \* 2 # 20 (escape * in some contexts)
math "10 * 2" # 20
math 20 / 4 # 5
math 17 % 5 # 2
# Math with variables
set num1 10
set num2 5
math $num1 + $num2 # 15
# Advanced math functions
math "sqrt(16)" # 4
math "sin(3.14159/2)" # 1
math "log(10)" # 2.30259
math "pow(2, 8)" # 256
# Floating point precision
math -s2 10 / 3 # 3.33 (2 decimal places)
File Operations and Globbing
fish
# Basic globbing
ls *.txt # All .txt files
ls **/*.py # All .py files recursively
ls file?.txt # file1.txt, file2.txt, etc.
# Advanced globbing patterns
ls *.(txt|md|rst) # Files with specific extensions
ls file[1-9].txt # file1.txt through file9.txt
# File test operations
if test -f "file.txt"
echo "File exists"
end
if test -d "directory"
echo "Directory exists"
end
if test -x "script.sh"
echo "File is executable"
end
# File manipulation
cp source.txt destination.txt
mv old_name.txt new_name.txt
rm unwanted_file.txt
mkdir new_directory
rmdir empty_directory
Process Management
fish
# Background jobs
command & # Run in background
jobs # List active jobs
fg %1 # Bring job 1 to foreground
bg %1 # Send job 1 to background
kill %1 # Kill job 1
# Process information
ps aux # List all processes
pgrep firefox # Find process by name
pkill firefox # Kill process by name
# Disown processes
command &
disown # Detach from shell
Input/Output Redirection
fish
# Output redirection
command > file.txt # Redirect stdout to file
command >> file.txt # Append stdout to file
command 2> error.log # Redirect stderr to file
command &> output.log # Redirect both stdout and stderr
# Input redirection
command < input.txt # Read input from file
# Pipes
ls -l | grep "txt" # Pipe output to grep
ps aux | grep "firefox" | wc -l # Count firefox processes
# Tee command
command | tee file.txt # Write to file and stdout
Scripting Best Practices
Script Structure
fish
#!/usr/bin/env fish
# Script description
# Author: Your Name
# Date: YYYY-MM-DD
# Function definitions
function usage
echo "Usage: $argv[0] <option> <argument>"
echo "Options:"
echo " -h, --help Show this help"
echo " -v, --verbose Verbose output"
end
function main
# Parse arguments
argparse 'h/help' 'v/verbose' -- $argv
or return
if set -q _flag_help
usage
return 0
end
if set -q _flag_verbose
set -g verbose true
end
# Main script logic here
echo "Script execution completed"
end
# Call main function with all arguments
main $argv
Error Handling
fish
# Check command success
if command_that_might_fail
echo "Command succeeded"
else
echo "Command failed with status $status"
exit 1
end
# Validate arguments
function validate_file
set -l file $argv[1]
if not test -f "$file"
echo "Error: File '$file' does not exist" >&2
return 1
end
return 0
end
# Use in script
if not validate_file "important.txt"
exit 1
end
Performance Considerations
fish
# Use built-in string operations instead of external commands
set result (string replace "old" "new" $text) # Instead of sed
set length (string length $text) # Instead of wc
# Avoid unnecessary command substitutions
if test -f "file.txt" # Instead of if test (ls file.txt)
echo "File exists"
end
# Use arrays efficiently
set files *.txt
for file in $files # Instead of for file in (ls *.txt)
process_file $file
end
Debugging and Troubleshooting
Debug Mode
fish
# Run script with debug output
fish -d 3 script.fish
# Enable debug in script
set fish_trace 1
# Your commands here
set fish_trace 0
# Function debugging
function debug_function
echo "Function called with arguments: $argv" >&2
echo "Current directory: "(pwd) >&2
echo "Status: $status" >&2
end
Common Issues and Solutions
fish
# Issue: Command not found
# Solution: Check PATH and command existence
if not command -v mycommand >/dev/null
echo "mycommand is not installed or not in PATH"
exit 1
end
# Issue: Variable not expanding
# Solution: Use proper Fish syntax
set var "value"
echo $var # Correct
echo "$var" # Also correct
echo '$var' # Wrong - literal string
# Issue: Function not found
# Solution: Check function definition and loading
functions myfunction # Check if function exists
funcsave myfunction # Save function permanently
Performance Profiling
fish
# Time command execution
time command
# Profile function execution
function profile_function
set -l start_time (date +%s%N)
your_function $argv
set -l end_time (date +%s%N)
set -l duration (math "($end_time - $start_time) / 1000000")
echo "Function took $duration ms"
end
Fish Shell represents a paradigm shift in shell design, prioritizing user experience and discoverability over strict POSIX compliance. Its intelligent autosuggestions, syntax highlighting, and web-based configuration make it particularly appealing to newcomers to the command line, while its powerful scripting capabilities and extensive plugin ecosystem satisfy the needs of advanced users. Although its different syntax requires some adjustment for users coming from traditional shells, Fish's focus on being helpful and intuitive makes it an excellent choice for interactive shell use and modern development workflows.