Fish Shell Tips

Useful tips for working with the fish shell. Fish syntax differs from bash/zsh in several key ways — no export, different variable scoping, and cleaner loop/conditional syntax. For the full reference, see the official fish documentation.

PATH Management

Add to PATH

Use fish_add_path to add a directory. It updates fish_user_paths persistently and avoids duplicates:

fish_add_path /path/to/add

Use -v to print the underlying set command that is executed, and verify the result:

fish_add_path -v /path/to/add
echo $fish_user_paths | tr " " "\n" | nl

View current PATH entries

echo $fish_user_paths | tr " " "\n" | nl

Remove from PATH

Use contains -i to look up the index of the entry you want to remove and erase it in one step:

set --erase --universal fish_user_paths[(contains -i /path/to/remove $fish_user_paths)]

This avoids the two-step process of listing entries and manually entering the index number.

Note: fish_user_paths is a universal variable, meaning changes persist across all fish sessions automatically.

Environment Variables

Fish uses set instead of export. The scope flag controls persistence:

# Session-only (lost when shell exits) — equivalent to bash's plain assignment
set MY_VAR value

# Export to child processes (equivalent to bash's export)
set -x MY_VAR value

# Persist across sessions and all fish windows (universal)
set -Ux MY_VAR value

Unset a variable

set -e MY_VAR
Tip: Universal variables (-U) are stored in ~/.config/fish/fish_variables and shared across all fish sessions — no need to put them in config.fish.

Loops

Fish uses for … in … end instead of bash's do … done:

for f in *.txt
    echo $f
end

Loop over a sequence of numbers with seq:

for i in (seq 1 5)
    echo $i
end

while loops follow the same end pattern:

while test -f lockfile
    sleep 1
end

Conditionals

Fish uses if … else … end — no brackets required, just a command whose exit status is tested:

if test -d ~/projects
    echo "projects dir exists"
else
    echo "missing"
end

String comparison uses = (one equals sign) inside test, or the string builtin:

if test "$STATUS" = "ok"
    echo "all good"
end

and / or replace && / || as statement combiners:

test -f config.yaml; and echo "found" ; or echo "not found"
Note: Fish does not use [[ ]] or [ ] for tests — use the test builtin or string for string operations.

Customizing the Prompt

The easiest way to pick a prompt theme is the built-in web UI:

fish_config

This opens a browser tab where you can preview and select prompt themes, colors, and more — no config editing required.

For full control, define a fish_prompt function. Fish looks for it in ~/.config/fish/functions/fish_prompt.fish:

# ~/.config/fish/functions/fish_prompt.fish
function fish_prompt
    set_color cyan
    echo -n (prompt_pwd)   # abbreviated current directory
    set_color normal
    echo -n ' > '
end

Useful builtins for prompt building:

  • prompt_pwd — current directory, shortened (respects fish_prompt_pwd_dir_length)
  • set_color — set foreground/background color; set_color normal resets
  • fish_git_prompt — git branch and status string (enable with set -g __fish_git_prompt_showdirtystate 1)
Tip: A separate fish_right_prompt function populates the right side of the prompt line — handy for git status or timestamps without cluttering the left.

Oh My Fish

Oh My Fish (omf) is a package manager for fish that makes it easy to install prompt themes and plugins. Install it with:
curl https://raw.githubusercontent.com/oh-my-fish/oh-my-fish/master/bin/install | fish
Then browse and install themes (omf install bobthefish is a popular choice) or plugins (omf install z for directory jumping). Run omf help to see available commands, or visit the omf repository for a full list of packages.