# Install via Homebrew
brew install fish
# Set Fish as default shell
echo /opt/homebrew/bin/fish | sudo tee -a /etc/shells
chsh -s /opt/homebrew/bin/fish
# Ubuntu/Debian
sudo apt-add-repository ppa:fish-shell/release-3
sudo apt update
sudo apt install fish
# Fedora
sudo dnf install fish
# Arch Linux
sudo pacman -S fish
# Set Fish as default shell
chsh -s /usr/bin/fish
# Install via Windows Subsystem for Linux (WSL)
sudo apt install fish
# Or use Chocolatey
choco install fish
# Or use winget
winget install Fish.fish
# Run Fish in Docker
docker run -it ghcr.io/fish-shell/fish
# Add single directory to PATH
fish_add_path /usr/local/bin
# Add multiple directories
fish_add_path /usr/local/bin ~/.local/bin
# Prepend to PATH (default)
fish_add_path /opt/custom/bin
# Append to PATH instead of prepend
fish_add_path --append /opt/legacy/bin
# Add only if directory exists
if test -d ~/.cargo/bin
fish_add_path ~/.cargo/bin
end
# Common additions for development
fish_add_path ~/.local/bin # Local binaries
fish_add_path ~/.cargo/bin # Rust cargo
fish_add_path ~/.npm-global/bin # npm global packages
fish_add_path /usr/local/go/bin # Go binaries
fish_add_path $HOME/go/bin # Go workspace
fish_add_path ~/.rbenv/shims # Ruby rbenv
fish_add_path ~/.pyenv/shims # Python pyenv
# Add to PATH manually (old way, not recommended)
set -x PATH $PATH /new/path
# Prepend to PATH
set -x PATH /new/path $PATH
# Add multiple paths
set -x PATH /path/one /path/two $PATH
# Remove from PATH
set -x PATH (string match -v "/old/path" $PATH)
# Add to ~/.config/fish/config.fish for persistence
if status is-interactive
# Add custom paths
fish_add_path ~/.local/bin
fish_add_path ~/.cargo/bin
fish_add_path ~/bin
end
# Set a simple variable
set myvar "hello"
# Set multiple variables at once
set x 1
set y 2
set z 3
# Set a variable with multiple values (list)
set fruits apple banana cherry
# Set a universal variable (persists across sessions)
set -U editor vim
# Set a global variable (available to all functions)
set -g config_path /etc/myapp
# Set a local variable (function scope only)
set -l temp_file /tmp/data.txt
# Export a variable (make it available to subprocesses)
set -x PATH $PATH /usr/local/bin
# Access a variable
echo $myvar
# Access list elements (1-indexed)
echo $fruits[1] # First element: apple
echo $fruits[2..3] # Range: banana cherry
echo $fruits[-1] # Last element: cherry
# Get the count of list items
echo (count $fruits)
# Check if variable is set
if set -q myvar
echo "myvar is set"
end
# Erase a variable
set -e myvar
# Erase a universal variable
set -e -U editor
# Simple function
function greet
echo "Hello, Fish!"
end
# Function with arguments
function greet_user
echo "Hello, $argv[1]!"
end
# Function with multiple arguments
function add
math $argv[1] + $argv[2]
end
# Function with description (shows in help)
function deploy --description "Deploy the application"
echo "Deploying..."
# deployment logic here
end
# Function with argument names (documentation only)
function create_user --argument name email
echo "Creating user: $name ($email)"
end
# Access all arguments
function print_args
echo "All arguments: $argv"
end
# Access specific arguments
function greet_full
echo "Hello, $argv[1] $argv[2]!"
end
# Count arguments
function arg_count
echo "You passed" (count $argv) "arguments"
end
# Check if arguments exist
function require_arg
if test (count $argv) -eq 0
echo "Error: No arguments provided"
return 1
end
echo "Processing: $argv[1]"
end
# Save function to file for autoloading
# File: ~/.config/fish/functions/myfunction.fish
function myfunction
echo "This function auto-loads!"
end
# List all functions
functions
# Show function definition
functions myfunction
# Erase a function
functions -e myfunction
# Simple if
if test $status -eq 0
echo "Success"
end
# If-else
if test -f ~/.config/fish/config.fish
echo "Config exists"
else
echo "Config not found"
end
# If-else if-else
if test $count -gt 10
echo "More than 10"
else if test $count -gt 5
echo "More than 5"
else
echo "5 or less"
end
# Using square brackets (shorthand for test)
if [ $USER = "root" ]
echo "Running as root"
end
# String comparisons
if test $name = "Alice"
echo "Hello Alice"
end
if test $name != "Bob"
echo "Not Bob"
end
# Numeric comparisons
if test $age -gt 18 # Greater than
echo "Adult"
end
if test $age -lt 13 # Less than
echo "Child"
end
if test $age -ge 18 # Greater than or equal
echo "Voting age"
end
if test $age -le 65 # Less than or equal
echo "Working age"
end
if test $age -eq 30 # Equal
echo "Exactly 30"
end
if test $age -ne 25 # Not equal
echo "Not 25"
end
# File tests
if test -f /path/to/file # File exists and is regular file
echo "File exists"
end
if test -d /path/to/dir # Directory exists
echo "Directory exists"
end
if test -e /path/to/something # Path exists (file or dir)
echo "Path exists"
end
if test -r /path/to/file # Readable
echo "Can read"
end
if test -w /path/to/file # Writable
echo "Can write"
end
if test -x /path/to/file # Executable
echo "Can execute"
end
# AND operator
if test $age -gt 18; and test $age -lt 65
echo "Working age"
end
# OR operator
if test $day = "Saturday"; or test $day = "Sunday"
echo "Weekend!"
end
# NOT operator
if not test -f ~/.ssh/id_rsa
echo "SSH key not found"
end
# Combining operators
if test $status -eq 0; and not test -z "$output"
echo "Command succeeded with output"
end
# Switch case
switch $animal
case cat
echo "Meow"
case dog
echo "Woof"
case bird
echo "Tweet"
case '*'
echo "Unknown animal"
end
# Multiple patterns per case
switch $extension
case jpg jpeg png gif
echo "Image file"
case mp4 avi mkv
echo "Video file"
case mp3 wav flac
echo "Audio file"
case '*'
echo "Unknown file type"
end
# Loop over list
for item in apple banana cherry
echo $item
end
# Loop over variable list
set fruits apple banana cherry
for fruit in $fruits
echo "I like $fruit"
end
# Loop over command output
for file in *.txt
echo "Processing $file"
end
# Loop over range (using seq)
for i in (seq 1 5)
echo "Number: $i"
end
# Loop with index
for i in (seq (count $items))
echo "Item $i: $items[$i]"
end
# Basic while loop
set i 1
while test $i -le 5
echo $i
set i (math $i + 1)
end
# While reading lines from file
while read -l line
echo "Line: $line"
end < input.txt
# Infinite loop with break
while true
echo "Press Ctrl+C to stop"
sleep 1
end
# Break - exit loop
for i in (seq 1 10)
if test $i -eq 5
break
end
echo $i
end
# Continue - skip to next iteration
for i in (seq 1 10)
if test (math $i % 2) -eq 0
continue # Skip even numbers
end
echo $i
end
# Capture command output
set current_dir (pwd)
set files (ls)
set date_str (date +%Y-%m-%d)
# Use in strings
echo "Today is $date_str"
# Use in conditions
if test (whoami) = "root"
echo "Running as root"
end
# Nested substitution
set user_home (echo ~(whoami))
# String length
set name "Alice"
echo (string length $name) # 5
# Substring
set str "Hello World"
echo (string sub -s 1 -l 5 $str) # Hello
echo (string sub -s 7 $str) # World
# Uppercase/lowercase
echo (string upper "hello") # HELLO
echo (string lower "WORLD") # world
# Trim whitespace
set text " hello "
echo (string trim $text) # hello
echo (string trim -l $text) # "hello " (left trim)
echo (string trim -r $text) # " hello" (right trim)
# Match pattern
if string match -q "*.txt" $filename
echo "Text file"
end
# Match with regex
if string match -qr '^\d+$' $input
echo "It's a number"
end
# Replace string
set path "/home/user/documents"
echo (string replace "/home/user" "~" $path) # ~/documents
# Replace all occurrences
set text "foo bar foo"
echo (string replace -a "foo" "baz" $text) # baz bar baz
# Split string
set csv "apple,banana,cherry"
set items (string split "," $csv)
echo $items[1] # apple
# Join strings
set items apple banana cherry
echo (string join ", " $items) # apple, banana, cherry
# Create list
set colors red green blue
# Append to list
set colors $colors yellow
# Prepend to list
set colors orange $colors
# Access elements
echo $colors[1] # First element
echo $colors[-1] # Last element
echo $colors[2..4] # Range
# List length
echo (count $colors)
# Check if item in list
if contains red $colors
echo "Red is in the list"
end
# Remove duplicates
set numbers 1 2 2 3 3 3 4
set unique (echo $numbers | tr ' ' '\n' | sort -u)
# Reverse list
set reversed $colors[-1..1]
# Read user input
read -P "Enter your name: " name
echo "Hello, $name"
# Read with prompt and local variable
read -l -P "Enter password: " password
# Read line by line from file
while read -l line
echo "Line: $line"
end < file.txt
# Read from stdin
cat file.txt | while read -l line
echo "Processing: $line"
end
# Print to stdout
echo "Normal output"
# Print to stderr
echo "Error message" >&2
# Suppress output
command > /dev/null 2>&1
# Append to file
echo "Log entry" >> logfile.txt
# Write to file (overwrite)
echo "New content" > file.txt
# Pipe output to another command
ls -l | grep ".txt"
# Redirect stdout to file
echo "Hello" > output.txt
# Redirect stderr to file
command 2> errors.txt
# Redirect both stdout and stderr
command > output.txt 2>&1
# Append to file
echo "More data" >> output.txt
# Pipe stderr
command 2>&1 | grep "error"
# Background job
sleep 10 &
# List background jobs
jobs
# Wait for job to complete
wait
# Kill job
kill %1
# Command chaining
command1; and command2 # Run command2 only if command1 succeeds
command1; or command2 # Run command2 only if command1 fails
# Set environment variable
set -x DATABASE_URL "postgresql://localhost/mydb"
# Set PATH
set -x PATH $PATH /usr/local/bin
# Remove from PATH
set -x PATH (string match -v "/old/path" $PATH)
# Set for single command
env VAR=value command
# View all environment variables
env
# View specific variable
echo $PATH
# Main config: ~/.config/fish/config.fish
# Set greeting
set fish_greeting "Welcome to Fish!"
# Or disable greeting
set fish_greeting
# Add to PATH
fish_add_path /usr/local/bin
fish_add_path ~/.local/bin
# Set environment variables
set -x EDITOR vim
set -x VISUAL code
# Create alias (use abbreviation instead)
abbr -a ll 'ls -lah'
abbr -a gst 'git status'
abbr -a gco 'git checkout'
# List abbreviations
abbr -l
# Erase abbreviation
abbr -e ll
# Custom 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
# Change directory
cd /path/to/directory
# Go to home
cd ~
# Go to previous directory
cd -
# Push directory to stack
pushd /path/to/dir
# Pop directory from stack
popd
# List directory stack
dirs
# Copy files
cp source.txt destination.txt
# Move/rename files
mv old.txt new.txt
# Remove files
rm file.txt
# Remove directory recursively
rm -rf directory/
# Create directory
mkdir new_folder
# Create nested directories
mkdir -p path/to/nested/folder
# Find files
find . -name "*.fish"
# Search file contents
grep "pattern" file.txt
# Search recursively
grep -r "pattern" directory/
# Basic arithmetic
math 5 + 3 # 8
math 10 - 4 # 6
math 6 '*' 7 # 42 (quote * to prevent glob)
math 15 / 3 # 5
math 17 % 5 # 2 (modulo)
# Floating point
math 10 / 3 # 3.333333
# Power
math "2 ^ 8" # 256
# Using in variables
set result (math $a + $b)
# Increment variable
set count (math $count + 1)
# Run when variable changes
function on_pwd_change --on-variable PWD
echo "Directory changed to $PWD"
end
# Run on Fish exit
function on_exit --on-event fish_exit
echo "Goodbye!"
end
# Run on prompt
function before_prompt --on-event fish_prompt
# Do something before each prompt
end
# Define custom completion
complete -c mycommand -s h -l help -d "Show help"
complete -c mycommand -s v -l version -d "Show version"
complete -c mycommand -s o -l output -r -d "Output file"
# File completion
complete -c mycommand -a '(__fish_complete_path)'
# Disable file completion
complete -c mycommand -f